MISE A JOUR-no-op dans l'instruction SQL MERGE
-
24-10-2019 - |
Question
J'ai une table avec des données persistantes en elle. Maintenant, quand je fais une recherche, j'ai aussi un CTE assez complexe qui calcule les valeurs requises pour le résultat et je dois insérer des lignes manquantes dans la table persistante. En fin de compte, je veux choisir le résultat composé de toutes les lignes identifiées par le CTE, mais avec les données de la table si elles étaient déjà dans la table, et je besoin des informations si une ligne a été juste inséré ou non.
simplifié ce fonctionne comme ceci (les pistes de code suivant comme une requête normale si vous voulez l'essayer):
-- Set-up of test data, this would be the persisted table
DECLARE @target TABLE (id int NOT NULL PRIMARY KEY) ;
INSERT INTO @target (id) SELECT v.id FROM (VALUES (1), (2)) v(id);
-- START OF THE CODE IN QUESTION
-- The result table variable (will be several columns in the end)
DECLARE @result TABLE (id int NOT NULL, new bit NOT NULL) ;
WITH Source AS (
-- Imagine a fairly expensive, recursive CTE here
SELECT * FROM (VALUES (1), (3)) AS Source (id)
)
MERGE INTO @target AS Target
USING Source
ON Target.id = Source.id
-- Perform a no-op on the match to get the output record
WHEN MATCHED THEN
UPDATE SET Target.id=Target.id
WHEN NOT MATCHED BY TARGET THEN
INSERT (id) VALUES (SOURCE.id)
-- select the data to be returned - will be more columns
OUTPUT source.id, CASE WHEN $action='INSERT' THEN CONVERT(bit, 1) ELSE CONVERT(bit, 0) END
INTO @result ;
-- Select the result
SELECT * FROM @result;
Je ne suis pas comme la partie WHEN MATCHED THEN UPDATE
, je préfère laisser la mise à jour redondante loin mais je ne suis pas la ligne de résultat dans la clause OUTPUT
.
Est-ce la façon la plus efficace de faire ce genre de remplir et de renvoyer des données?
Ou y aurait-il une solution plus efficace sans MERGE
, par exemple par pré-calcul du résultat avec un SELECT
puis effectuer une INSERT
des lignes qui sont new=0
? J'ai des difficultés d'interprétation du plan de requête, car il se résume essentiellement à un « index cluster fusion » qui est assez vague pour moi par rapport à la performance sage de la SELECT
séparée suivie par la variante de INSERT
. Et je me demande si SQL Server (2008 R2 avec CU1) est en fait assez intelligent pour voir que le UPDATE
est un no-op (par exemple pas d'écriture nécessaire).
La solution
Vous pouvez déclarer une variable factice et sa valeur dans la clause, égalés.
DECLARE @dummy int;
...
MERGE
...
WHEN MATCHED THEN
UPDATE SET @dummy = 0
...
Je crois qu'il devrait être moins cher que la mise à jour de la table réelle.