J'essaie d'utiliser une MERGE
instruction pour insérer ou supprimer des lignes d'une table, mais je souhaite uniquement agir sur un sous-ensemble de ces lignes. La documentation pour MERGE
a un avertissement assez fort libellé:
Il est important de spécifier uniquement les colonnes de la table cible utilisées à des fins de correspondance. Autrement dit, spécifiez les colonnes de la table cible qui sont comparées à la colonne correspondante de la table source. N'essayez pas d'améliorer les performances des requêtes en filtrant les lignes de la table cible dans la clause ON, par exemple en spécifiant AND NOT target_table.column_x = value. Cela pourrait retourner des résultats inattendus et incorrects.
mais c'est exactement ce qu'il me semble devoir faire pour faire mon MERGE
travail.
Les données que j'ai sont une table standard de jointure plusieurs-à-plusieurs d'éléments (par exemple, quels éléments sont inclus dans quelles catégories) comme ceci:
CategoryId ItemId
========== ======
1 1
1 2
1 3
2 1
2 3
3 5
3 6
4 5
Ce que je dois faire est de remplacer efficacement toutes les lignes d'une catégorie spécifique par une nouvelle liste d'éléments. Ma tentative initiale de faire ceci ressemble à ceci:
MERGE INTO CategoryItem AS TARGET
USING (
SELECT ItemId FROM SomeExternalDataSource WHERE CategoryId = 2
) AS SOURCE
ON SOURCE.ItemId = TARGET.ItemId AND TARGET.CategoryId = 2
WHEN NOT MATCHED BY TARGET THEN
INSERT ( CategoryId, ItemId )
VALUES ( 2, ItemId )
WHEN NOT MATCHED BY SOURCE AND TARGET.CategoryId = 2 THEN
DELETE ;
Cela semble fonctionner dans mes tests, mais je fais exactement ce que MSDN m'avertit explicitement de ne pas le faire. Cela me fait craindre des problèmes inattendus plus tard, mais je ne vois pas d'autre moyen de MERGE
ne modifier que les lignes affectées par la valeur de champ spécifique ( CategoryId = 2
) et d'ignorer les lignes d'autres catégories.
Existe-t-il une méthode "plus correcte" pour obtenir le même résultat? Et quels sont les "résultats inattendus ou incorrects" sur lesquels MSDN m'avertit?
la source
Réponses:
L'
MERGE
instruction a une syntaxe complexe et une implémentation encore plus complexe, mais il s'agit essentiellement de joindre deux tables, de filtrer les lignes à modifier (insérées, mises à jour ou supprimer), puis d'effectuer les modifications demandées. À partir des exemples de données suivants:Cible
La source
Le résultat souhaité est de remplacer les données de la cible par des données de la source, mais uniquement pour
CategoryId = 2
. Après la descriptionMERGE
donnée ci-dessus, nous devrions écrire une requête qui joint la source et la cible sur les clés uniquement, et filtrer les lignes uniquement dans lesWHEN
clauses:Cela donne les résultats suivants:
Le plan d'exécution est le suivant:
Notez que les deux tables sont entièrement numérisées. Nous pourrions penser que cela est inefficace, car seules les lignes où
CategoryId = 2
seront affectées la table cible. C’est ici que les avertissements de la documentation en ligne entrent en jeu. Une tentative erronée d’optimisation pour ne toucher que les lignes nécessaires de la cible est la suivante:La logique de la
ON
clause est appliquée dans le cadre de la jointure. Dans ce cas, la jointure est une jointure externe complète (voir pourquoi cette entrée dans la documentation en ligne ). L'application de la vérification de la catégorie 2 sur les lignes cibles dans le cadre d'une jointure externe entraîne la suppression des lignes de valeur différente (car elles ne correspondent pas à la source):La cause fondamentale est la même raison pour laquelle les prédicats se comportent différemment dans une
ON
clause de jointure externe que s'ils le spécifient dans laWHERE
clause. LaMERGE
syntaxe (et l'implémentation de la jointure en fonction des clauses spécifiées) rendent simplement plus difficile de voir qu'il en est ainsi.Les instructions de la documentation en ligne (développées dans l' entrée Optimizing Performance ) fournissent des indications permettant de s'assurer que la sémantique correcte est exprimée à l'aide de la
MERGE
syntaxe, sans que l'utilisateur ait nécessairement à comprendre tous les détails de la mise en œuvre ni à rendre compte de la manière dont l'optimiseur pourrait légitimement réorganiser. les choses pour des raisons d'efficacité d'exécution.La documentation offre trois méthodes possibles pour implémenter le filtrage précoce:
La spécification d'une condition de filtrage dans la
WHEN
clause garantit des résultats corrects, mais peut signifier que davantage de lignes lues et traitées à partir des tables source et cible sont strictement nécessaires (comme le montre le premier exemple).La mise à jour via une vue contenant la condition de filtrage garantit également des résultats corrects (puisque les lignes modifiées doivent être accessibles pour la mise à jour via la vue), mais cela nécessite une vue dédiée et une condition qui suit les conditions impaires pour la mise à jour des vues.
L'utilisation d'une expression de table commune comporte des risques similaires à l'ajout de prédicats à la
ON
clause, mais pour des raisons légèrement différentes. Dans de nombreux cas, cela sera sûr, mais cela nécessite une analyse experte du plan d'exécution (ainsi que des tests pratiques approfondis). Par exemple:Cela produit des résultats corrects (non répétés) avec un plan plus optimal:
Le plan ne lit que les lignes de la catégorie 2 à partir de la table cible. Cela peut constituer un facteur de performance important si la table cible est grande, mais il est trop facile de se tromper en utilisant la
MERGE
syntaxe.Parfois, il est plus facile d’écrire les
MERGE
opérations DML distinctes. Cette approche peut même donner de meilleurs résultats qu’une seuleMERGE
, ce qui surprend souvent les gens.la source