Ajouté 7/11 Le problème est que des blocages se produisent en raison de l'analyse d'index pendant MERGE JOIN. Dans ce cas, une transaction tente d'obtenir le verrou S sur tout l'index dans la table parent FK, mais auparavant, une autre transaction place le verrou X sur une valeur clé de l'index.
Permettez-moi de commencer par un petit exemple (TSQL2012 DB de 70-461 cource utilisé):
CREATE TABLE [Sales].[Orders](
[orderid] [int] IDENTITY(1,1) NOT NULL,
[custid] [int] NULL,
[empid] [int] NOT NULL,
[shipperid] [int] NOT NULL,
... )
Les colonnes [custid], [empid], [shipperid]
sont des paramètres corelés pour en [Sales].[Customers], [HR].[Employees], [Sales].[Shippers]
conséquence. Dans chaque cas, nous avons un index clusterisé sur une colonne référencée dans une table parrent.
ALTER TABLE [Sales].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([custid]) REFERENCES [Sales].[Customers] ([custid])
ALTER TABLE [Sales].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Employees] FOREIGN KEY([empid]) REFERENCES [HR].[Employees] ([empid])
ALTER TABLE [Sales].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Shippers] FOREIGN KEY([shipperid])REFERENCES [Sales].[Shippers] ([shipperid])
J'essaie une INSERT [Sales].[Orders] SELECT ... FROM
autre table appelée [Sales].[OrdersCache]
qui a la même structure que les [Sales].[Orders]
clés étrangères sauf. Une autre chose peut être importante pour mentionner la table [Sales].[OrdersCache]
est un index clusterisé.
CREATE CLUSTERED INDEX idx_c_OrdersCache ON Sales.OrdersCache ( custid, empid )
Comme prévu lorsque j'essaie d'insérer un faible volume de données, LOOP JOIN fonctionne très bien en faisant une recherche d'index sur les clés étrangères.
Avec des volumes de données élevés, MERGE JOIN est utilisé par l'optimiseur de requêtes comme un moyen le plus efficace de conserver la clé foregn dans la requête.
Et il n'y a rien à voir avec cela, sauf en utilisant OPTION (LOOP JOIN) dans notre cas avec des clés étrangères ou INNER LOOP JOIN dans le cas explicite JOIN.
Voici la requête que j'essaie d'exécuter dans mon environnement:
INSERT Sales.Orders (
custid, empid, shipperid, ... )
SELECT custid, empid, 2, ...
FROM Sales.OrdersCache
En regardant le plan, nous pouvons voir que les 3 clés étrangères validées avec MERGE JOIN. Ce n'est pas un moyen approprié pour moi car il utilise INDEX SCAN avec verrouillage de l'index entier.
L'utilisation d'OPTION (LOOP JOIN) n'est pas appropriée car elle coûte près de 15% de plus que MERGE JOIN (je pense que la régression sera plus importante avec l'augmentation des volumes de données).
Dans l'instruction SELECT, vous pouvez voir une seule valeur d' shipperid
attribut pour l'ensemble inséré. À mon avis, il doit y avoir un moyen d'accélérer la phase de validation de l'ensemble inséré au moins pour l'attribut immuable. Quelque chose comme:
- faire LOOP JOIN, MERGE JOIN, HASH JOIN si nous avons un sous-ensemble non défini pour la validation JOIN
- s'il n'y a qu'une seule valeur explicite de la colonne validée, nous ne faisons la validation qu'une seule fois (INDEX SEEK).
Existe-t-il un modèle commun pour contourner la situation ci-dessus en utilisant des structures de code, des objets DDL supplémentaires, etc.?
Ajouté le 20/07. Solution. Query Optimizer effectue déjà une optimisation de validation «clé unique - clé étrangère» en utilisant MERGE JOIN. Et fait uniquement pour la table Sales.Shippers, laissant LOOP JOIN pour une autre jointure dans la requête en même temps. Étant donné que j'ai quelques lignes dans la table parent, l'Optimiseur de requête utilise l'algorithme de jointure Tri-fusion et compare chaque ligne de la table interne avec la table parent une seule fois. C'est donc la réponse à ma question s'il existe un mécanisme particulier pour traiter efficacement les valeurs uniques dans un ensemble lors de la validation de clé unique. Ce n'est pas une décision si parfaite, mais c'est ainsi que SQL Server optimise le cas.
L'enquête sur les performances a révélé que dans mon cas, l'instruction d'insertion MERGE JOIN et LOOP JOIN est devenue approximativement égale à 750 lignes insérées simultanément avec la supériorité suivante de MERGE JOIN (en ressource de temps CPU). L'utilisation d'OPTION (LOOP JOIN) est donc une solution appropriée pour mon processus métier.