Hinzugefügt 7/11 Das Problem ist, dass Deadlocks aufgrund von Index-Scans während MERGE JOIN auftreten. In diesem Fall setzt eine Transaktion, die versucht, eine S-Sperre für den gesamten Index in der übergeordneten FK-Tabelle zu erhalten, aber zuvor eine andere Transaktion eine X-Sperre für einen Schlüsselwert des Index.
Lassen Sie mich mit einem kleinen Beispiel beginnen (TSQL2012 DB von 70-461 Cource verwendet):
CREATE TABLE [Sales].[Orders](
[orderid] [int] IDENTITY(1,1) NOT NULL,
[custid] [int] NULL,
[empid] [int] NOT NULL,
[shipperid] [int] NOT NULL,
... )
Spalten [custid], [empid], [shipperid]
sind entsprechend korrelierte Parameter für [Sales].[Customers], [HR].[Employees], [Sales].[Shippers]
. In jedem Fall haben wir einen Clustered-Index für eine referenzierte Spalte in einer parrent-Tabelle.
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])
Ich versuche, eine INSERT [Sales].[Orders] SELECT ... FROM
andere Tabelle namens aufzurufen, [Sales].[OrdersCache]
die die gleiche Struktur wie die [Sales].[Orders]
außer Fremdschlüssel hat. Eine andere Sache könnte wichtig sein, um zu erwähnen, dass die Tabelle [Sales].[OrdersCache]
ein Clustered-Index ist.
CREATE CLUSTERED INDEX idx_c_OrdersCache ON Sales.OrdersCache ( custid, empid )
Wie erwartet, wenn ich versuche, ein geringes Datenvolumen einzufügen, funktioniert LOOP JOIN einwandfrei, sodass die Indexsuche für die Fremdschlüssel durchgeführt wird.
Bei hohen Datenmengen wird MERGE JOIN vom Abfrageoptimierer als effizienteste Methode zur Verwaltung des foregn-Schlüssels in der Abfrage verwendet.
Und es hat nichts damit zu tun, außer OPTION (LOOP JOIN) in unserem Fall mit Fremdschlüsseln oder INNER LOOP JOIN in expliziten JOIN-Fällen.
Unten ist die Abfrage, die ich in meiner Umgebung ausführen möchte:
INSERT Sales.Orders (
custid, empid, shipperid, ... )
SELECT custid, empid, 2, ...
FROM Sales.OrdersCache
Wenn wir uns den Plan ansehen, können wir sehen, dass alle 3 Foreing-Schlüssel mit MERGE JOIN validiert wurden. Dies ist für mich kein geeigneter Weg, da INDEX SCAN mit vollständiger Indexsperre verwendet wird.
Die Verwendung von OPTION (LOOP JOIN) ist nicht geeignet, da sie fast 15% mehr kostet als MERGE JOIN (ich denke, die Regression wird mit zunehmendem Datenvolumen größer sein).
In der SELECT-Anweisung sehen Sie einen einzelnen Wert für das shipperid
Attribut für die gesamte eingefügte Menge. Meiner Meinung nach muss es eine Möglichkeit geben, die Validierungsphase für den eingefügten Satz zumindest für das unveränderliche Attribut zu beschleunigen. Etwas wie:
- Machen Sie LOOP JOIN, MERGE JOIN, HASH JOIN, wenn wir eine undefinierte Teilmenge für die JOIN-Validierung haben
- Wenn es nur einen einzigen expliziten Wert für die validierte Spalte gibt, führen wir die Validierung nur einmal durch (INDEX SEEK).
Gibt es ein allgemeines Muster, um die oben genannte Situation mithilfe von Codestrukturen, zusätzlichen DDL-Objekten usw. zu überwinden?
Hinzugefügt 20/07. Lösung. Das Abfrageoptimierungsprogramm führt bereits mithilfe von MERGE JOIN eine Validierungsoptimierung für "Einzelschlüssel - Fremdschlüssel" durch. Dies gilt nur für die Tabelle "Sales.Shippers", wobei LOOP JOIN gleichzeitig für weitere Verknüpfungen in der Abfrage verbleibt. Da ich einige Zeilen in der übergeordneten Tabelle habe, verwendet Query Optimizer den Sort-Merge-Join-Algorithmus und vergleicht jede Zeile in der inneren Tabelle nur einmal mit der übergeordneten Tabelle. Das ist also die Antwort auf meine Frage, ob es einen bestimmten Mechanismus gibt, um einzelne Werte in einem Satz während der Einzelschlüsselvalidierung effektiv zu verarbeiten. Das ist keine so perfekte Entscheidung, aber so optimiert SQL Server den Fall.
Die Untersuchung der Leistungsbeeinträchtigung ergab, dass in meinem Fall die Einfügeanweisung MERGE JOIN und LOOP JOIN ungefähr gleich 750 gleichzeitig eingefügten Zeilen mit der folgenden Überlegenheit von MERGE JOIN (in der CPU-Zeitressource) wurde. Die Verwendung von OPTION (LOOP JOIN) ist daher eine geeignete Lösung für meinen Geschäftsprozess.