Um padrão bastante comum no aplicativo de banco de dados com o qual trabalho é a necessidade de criar um procedimento armazenado para um relatório ou utilitário que tenha um "modo de visualização". Quando esse procedimento faz atualizações, esse parâmetro indica que os resultados da ação devem ser retornados, mas o procedimento não deve realmente executar as atualizações no banco de dados.
Uma maneira de conseguir isso é simplesmente escrever uma if
instrução para o parâmetro e ter dois blocos de código completos; um deles atualiza e retorna dados e o outro apenas retorna os dados. Mas isso é indesejável devido à duplicação de código e a um nível relativamente baixo de confiança de que os dados de visualização são na verdade um reflexo preciso do que aconteceria com uma atualização.
O exemplo a seguir tenta aproveitar pontos de salvamento e variáveis de transação (que não são afetados por transações, em contraste com as tabelas temporárias que são) para usar apenas um único bloco de código para o modo de visualização como o modo de atualização ao vivo.
Nota: Reversões de transação não são uma opção, pois essa chamada de procedimento pode ser aninhada em uma transação. Isso é testado no 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
Estou procurando feedback sobre esse código e padrão de design e / ou se existem outras soluções para o mesmo problema em diferentes formatos.