Je ne suis pas complètement sûr, mais j'ai l'impression que cette question est vraiment upsert, qui est la suivante opération atomique:
- Si la ligne existe dans la source et la cible,
UPDATE
de la cible;
- Si la ligne n'existe que dans la source,
INSERT
de la ligne dans la cible;
- (En option) Si la ligne existe dans la cible, mais pas la source,
DELETE
de la ligne de la cible.
Les développeurs de-tourner-Administrateurs de bases de données souvent naïvement à écrire ligne par ligne, comme ceci:
-- For each row in source
IF EXISTS(<target_expression>)
IF @delete_flag = 1
DELETE <target_expression>
ELSE
UPDATE target
SET <target_columns> = <source_values>
WHERE <target_expression>
ELSE
INSERT target (<target_columns>)
VALUES (<source_values>)
C'est à peu près la pire chose que vous pouvez faire, et ce pour plusieurs raisons:
Il a une course à condition. La ligne peut disparaître entre IF EXISTS
et ultérieure DELETE
ou UPDATE
.
C'est un gaspillage. Pour chaque opération, vous avez un supplément opération en cours d'exécution; c'est peut-être trivial, mais cela dépend de comment vous vous êtes bien indexé.
Le pire de tout c'est à la suite d'un modèle itératif, une réflexion sur ces problèmes au niveau d'une seule ligne. Ce sera la plus grande (la pire) impact sur la performance globale.
Un très mineur (et j'insiste sur le mineur), l'optimisation est de simplement tenter de l' UPDATE
de toute façon; si la ligne n'existe pas, @@ROWCOUNT
sera de 0, et vous pouvez alors "en toute sécurité" insérer:
-- For each row in source
BEGIN TRAN
UPDATE target
SET <target_columns> = <source_values>
WHERE <target_expression>
IF (@@ROWCOUNT = 0)
INSERT target (<target_columns>)
VALUES (<source_values>)
COMMIT
Le pire des cas, ce sera toujours effectuer deux opérations pour chaque transaction, mais au moins il ya une chance de ne la faire qu'un, et il élimine également la condition de la course (genre de).
Mais le vrai problème est que c'est toujours effectué pour chaque ligne dans la source.
Avant de SQL Server 2008, vous avez eu à utiliser un maladroit 3-modèle des étapes à traiter au niveau du set (encore mieux que ligne par ligne):
BEGIN TRAN
INSERT target (<target_columns>)
SELECT <source_columns> FROM source s
WHERE s.id NOT IN (SELECT id FROM target)
UPDATE t SET <target_columns> = <source_columns>
FROM target t
INNER JOIN source s ON t.d = s.id
DELETE t
FROM target t
WHERE t.id NOT IN (SELECT id FROM source)
COMMIT
Comme je l'ai dit, la performance est assez moche, mais encore beaucoup mieux que celui-ligne-à-un-temps approche. SQL Server 2008, cependant, introduit enfin de FUSION de la syntaxe, alors maintenant, tout ce que vous avez à faire est ceci:
MERGE target
USING source ON target.id = source.id
WHEN MATCHED THEN UPDATE <target_columns> = <source_columns>
WHEN NOT MATCHED THEN INSERT (<target_columns>) VALUES (<source_columns>)
WHEN NOT MATCHED BY SOURCE THEN DELETE;
C'est tout. Une déclaration. Si vous utilisez SQL Server 2008 et besoin d'effectuer une séquence de INSERT
, UPDATE
et DELETE
selon si oui ou non la ligne existe déjà - même si c'est juste une ligne - il n'y a aucune excuse pour ne pas être l'aide d' MERGE
.
Vous pouvez même OUTPUT
lignes affectées par une MERGE
dans une variable de table si vous avez besoin de savoir par la suite ce qui a été fait. Simple, rapide et sans risque. Le faire.