Un patrón bastante común en la aplicación de base de datos con la que trabajo es la necesidad de crear un procedimiento almacenado para un informe o utilidad que tenga un "modo de vista previa". Cuando dicho procedimiento se actualiza, este parámetro indica que los resultados de la acción deben devolverse, pero el procedimiento no debe realizar las actualizaciones en la base de datos.
Una forma de lograr esto es simplemente escribir una if
declaración para el parámetro y tener dos bloques de código completos; uno de los cuales actualiza y devuelve datos y el otro solo devuelve los datos. Pero esto no es deseable debido a la duplicación de código y a un grado relativamente bajo de confianza de que los datos de vista previa son en realidad un reflejo preciso de lo que sucedería con una actualización.
El siguiente ejemplo intenta aprovechar los puntos de guardado y las variables de la transacción (que no se ven afectados por las transacciones, en contraste con las tablas temporales que sí lo están) para usar un solo bloque de código para el modo de vista previa como el modo de actualización en vivo.
Nota: Las reversiones de transacciones no son una opción, ya que esta llamada de procedimiento puede anidarse en una transacción. Esto se prueba en SQL Server 2012.
CREATE TABLE dbo.user_table (a int);
GO
CREATE PROCEDURE [dbo].[PREVIEW_EXAMPLE] (
@preview char(1) = 'Y'
) AS
CREATE TABLE #dataset_to_return (a int);
BEGIN TRANSACTION; -- preview mode required infrastructure
DECLARE @output_to_return TABLE (a int);
SAVE TRANSACTION savepoint;
-- do stuff here
INSERT INTO dbo.user_table (a)
OUTPUT inserted.a INTO @output_to_return (a)
VALUES (42);
-- catch preview mode
IF @preview = 'Y'
ROLLBACK TRANSACTION savepoint;
-- save output to temp table if used for return data
INSERT INTO #dataset_to_return (a)
SELECT a FROM @output_to_return;
COMMIT TRANSACTION;
SELECT a AS proc_return_data FROM #dataset_to_return;
RETURN 0;
GO
-- Examples
EXEC dbo.PREVIEW_EXAMPLE @preview = 'Y';
SELECT a AS user_table_after_preview_mode FROM user_table;
EXEC dbo.PREVIEW_EXAMPLE @preview = 'N';
SELECT a AS user_table_after_live_mode FROM user_table;
-- Cleanup
DROP TABLE dbo.user_table;
DROP PROCEDURE dbo.PREVIEW_EXAMPLE;
GO
Estoy buscando comentarios sobre este código y patrón de diseño, y / o si existen otras soluciones para el mismo problema en diferentes formatos.