Ich versuche einen Beispiel-Abfrageplan zu erstellen, um zu zeigen, warum die UNIONierung von zwei Ergebnismengen besser ist als die Verwendung von OR in einer JOIN-Klausel. Ein Abfrageplan, den ich geschrieben habe, hat mich ratlos gemacht. Ich verwende die StackOverflow-Datenbank mit einem nicht gruppierten Index für Users.Reputation.
CREATE NONCLUSTERED INDEX IX_NC_REPUTATION ON dbo.USERS(Reputation)
SELECT DISTINCT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
OR Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
Der Abfrageplan ist unter https://www.brentozar.com/pastetheplan/?id=BkpZU1MZE abrufbar. Die Abfragedauer beträgt für mich 4:37 Minuten, es werden 26612 Zeilen zurückgegeben.
Ich habe noch nie gesehen, dass ein solcher Konstantenscan aus einer vorhandenen Tabelle erstellt wurde. Ich bin nicht damit vertraut, warum ein Konstantenscan für jede einzelne Zeile ausgeführt wird, wenn normalerweise ein Konstantenscan für eine einzelne vom Benutzer eingegebene Zeile verwendet wird Zum Beispiel SELECT GETDATE (). Warum wird es hier verwendet? Ich würde mich sehr über eine Anleitung zum Lesen dieses Abfrageplans freuen.
Wenn ich dieses ODER in eine UNION aufspalte, wird ein Standardplan erstellt, der in 12 Sekunden ausgeführt wird und dieselben 26612 Zeilen zurückgibt.
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
WHERE Users.Reputation = 5
UNION
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
Ich interpretiere diesen Plan so:
- Alle 41782500 Zeilen aus Posts abrufen (die tatsächliche Anzahl der Zeilen entspricht dem CI-Scan für Posts)
- Für jede 41782500 Zeilen in Posts:
- Skalare erzeugen:
- Expr1005: OwnerUserId
- Expr1006: OwnerUserId
- Expr1004: Der statische Wert 62
- Expr1008: LastEditorUserId
- Expr1009: LastEditorUserId
- Expr1007: Der statische Wert 62
- In der Verkettung:
- Exp1010: Wenn Expr1005 (OwnerUserId) nicht null ist, verwenden Sie diese Option, andernfalls Expr1008 (LastEditorUserID).
- Expr1011: Wenn Expr1006 (OwnerUserId) nicht null ist, verwenden Sie dies, andernfalls verwenden Sie Expr1009 (LastEditorUserId).
- Expr1012: Wenn Expr1004 (62) null ist, verwende das, sonst verwende Expr1007 (62)
- Im Rechenskalar: Ich weiß nicht, was ein kaufmännisches Und tut.
- Beispiel1013: 4 [und?] 62 (Beispiel1012) = 4 und OwnerUserId IS NULL (NULL = Beispiel1010)
- Beispiel1014: 4 [und?] 62 (Beispiel1012)
- Beispiel1015: 16 und 62 (Beispiel1012)
- In der Reihenfolge nach sortieren nach:
- Expr1013 beschr
- Expr1014 Asc
- Expr1010 Asc
- Expr1015 beschr
- Im Zusammenführungsintervall wurden Expr1013 und Expr1015 entfernt (dies sind Eingaben, aber keine Ausgaben).
- In der Indexsuche unterhalb des Joins für verschachtelte Schleifen werden Expr1010 und Expr1011 als Suchprädikate verwendet, aber ich verstehe nicht, wie auf diese zugegriffen werden kann, wenn der Join für verschachtelte Schleifen von IX_NC_REPUTATION auf den Teilbaum mit Expr1010 und Expr1011 nicht ausgeführt wurde .
- Der Join mit verschachtelten Schleifen gibt nur die Users.IDs zurück, die mit dem vorherigen Teilbaum übereinstimmen. Aufgrund von Prädikat-Pushdown werden alle Zeilen zurückgegeben, die von der Indexsuche für IX_NC_REPUTATION zurückgegeben wurden.
- Der letzte Join mit verschachtelten Schleifen: Geben Sie für jeden Posts-Datensatz Users.Id aus, wobei im folgenden Datensatz eine Übereinstimmung gefunden wird.
SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id IN (Posts.OwnerUserId, Posts.LastEditorUserId) ) ;
SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND ( EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.OwnerUserId) OR EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.LastEditorUserId) ) ;