Un modèle assez courant dans l'application de base de données avec laquelle je travaille est la nécessité de créer une procédure stockée pour un rapport ou un utilitaire doté d'un "mode de prévisualisation". Lorsqu'une telle procédure effectue des mises à jour, ce paramètre indique que les résultats de l'action doivent être renvoyés, mais la procédure ne doit pas réellement effectuer les mises à jour de la base de données.
Une façon d'y parvenir est d'écrire simplement une if
instruction pour le paramètre et d'avoir deux blocs de code complets; dont l'un met à jour et renvoie les données et l'autre renvoie simplement les données. Mais cela n'est pas souhaitable en raison de la duplication de code et d'un degré relativement faible de confiance dans le fait que les données d'aperçu reflètent réellement ce qui se passerait avec une mise à jour.
L'exemple suivant tente de tirer parti des points de sauvegarde et des variables de transaction (qui ne sont pas affectés par les transactions, contrairement aux tables temporaires qui le sont) pour utiliser un seul bloc de code pour le mode d'aperçu comme mode de mise à jour en direct.
Remarque: les annulations de transaction ne sont pas une option car cet appel de procédure peut lui-même être imbriqué dans une transaction. Ceci est testé sur 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
Je recherche des commentaires sur ce code et ce modèle de conception, et / ou si d'autres solutions au même problème existent dans différents formats.