Dies hängt von den Daten in Ihren Tabellen, Ihren Indizes usw. ab. Schwer zu sagen, ohne die Ausführungspläne / die io + -Zeitstatistik vergleichen zu können.
Der Unterschied, den ich erwarten würde, ist die zusätzliche Filterung vor dem JOIN zwischen den beiden Tabellen. In meinem Beispiel habe ich die Aktualisierungen in Auswahl geändert, um meine Tabellen wiederzuverwenden.
Der Ausführungsplan mit "der Optimierung"
Ausführungsplan
Sie sehen deutlich, dass ein Filtervorgang stattfindet. In meinen Testdaten wurden keine Datensätze herausgefiltert und daher wurden keine Verbesserungen vorgenommen.
Der Ausführungsplan ohne "die Optimierung"
Ausführungsplan
Der Filter ist weg, was bedeutet, dass wir uns auf den Join verlassen müssen, um nicht benötigte Datensätze herauszufiltern.
Andere Gründe
Ein weiterer Grund / eine weitere Konsequenz für die Änderung der Abfrage könnte sein, dass beim Ändern der Abfrage ein neuer Ausführungsplan erstellt wurde, der zufällig schneller ist. Ein Beispiel hierfür ist die Engine, die einen anderen Join-Operator auswählt, aber das ist an dieser Stelle nur eine Vermutung.
BEARBEITEN:
Klarstellung nach Erhalt der beiden Abfragepläne:
Die Abfrage liest 550 Millionen Zeilen aus der großen Tabelle und filtert sie heraus.
Dies bedeutet, dass das Prädikat das meiste der Filterung ausführt, nicht das Suchprädikat. Dies führt dazu, dass die Daten gelesen, aber viel weniger zurückgegeben werden.
Wenn Sie SQL Server dazu bringen, einen anderen Index (Abfrageplan) zu verwenden, oder einen Index hinzufügen, kann dies behoben werden.
Warum hat die Optimierungsabfrage nicht dasselbe Problem?
Da ein anderer Abfrageplan verwendet wird, mit einem Scan anstelle einer Suche.
Ohne irgendwelche Suchvorgänge durchzuführen, sondern nur 4 Millionen Zeilen zurückzugeben, um damit zu arbeiten.
Nächster Unterschied
Ohne Berücksichtigung des Aktualisierungsunterschieds (bei der optimierten Abfrage wird nichts aktualisiert) wird für die optimierte Abfrage eine Hash-Übereinstimmung verwendet:
Anstelle eines verschachtelten Loop-Joins auf dem nicht optimierten:
Eine verschachtelte Schleife ist am besten, wenn eine Tabelle klein und die andere groß ist. Da beide nahe an der gleichen Größe liegen, würde ich argumentieren, dass das Hash-Match in diesem Fall die bessere Wahl ist.
Überblick
Die optimierte Abfrage
Der Plan der optimierten Abfrage weist Parallellismus auf, verwendet einen Hash-Match-Join und muss weniger Rest-E / A-Filterung durchführen. Außerdem wird eine Bitmap verwendet, um Schlüsselwerte zu entfernen, die keine Verknüpfungszeilen erzeugen können. (Auch wird nichts aktualisiert)
Die nicht optimierte Abfrage
Der Plan der nicht optimierten Abfrage weist keine Parallellismus auf, verwendet eine verschachtelte Schleifenverknüpfung und muss eine Rest-E / A-Filterung für 550 Millionen Datensätze durchführen. (Auch das Update findet statt)
Was können Sie tun, um die nicht optimierte Abfrage zu verbessern?
Ändern des Index in Vorname und Nachname in der Liste der Schlüsselspalten:
CREATE INDEX IX_largeTableOfPeople_birth_date_first_name_last_name auf dbo.largeTableOfPeople (Geburtsdatum, Vorname, Nachname) include (id)
Aufgrund der Verwendung von Funktionen und der Größe dieser Tabelle ist dies möglicherweise nicht die optimale Lösung.
- Aktualisieren der Statistiken mithilfe der Neukompilierung, um den besseren Plan zu erhalten.
- Hinzufügen von OPTION
(HASH JOIN, MERGE JOIN)
zur Abfrage
- ...
Testdaten + Abfragen verwendet
CREATE TABLE #smallTableOfPeople(importantValue int, birthDate datetime2, first_name varchar(50),last_name varchar(50));
CREATE TABLE #largeTableOfPeople(importantValue int, birth_date datetime2, first_name varchar(50),last_name varchar(50));
set nocount on;
DECLARE @i int = 1
WHILE @i <= 1000
BEGIN
insert into #smallTableOfPeople (importantValue,birthDate,first_name,last_name)
VALUES(NULL, dateadd(mi,@i,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @i += 1;
END
set nocount on;
DECLARE @j int = 1
WHILE @j <= 20000
BEGIN
insert into #largeTableOfPeople (importantValue,birth_Date,first_name,last_name)
VALUES(@j, dateadd(mi,@j,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @j += 1;
END
SET STATISTICS IO, TIME ON;
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å');
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
--AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å')
drop table #largeTableOfPeople;
drop table #smallTableOfPeople;
AND LEFT(TRIM(largeTbl.last_name), 1) BETWEEN 'a' AND 'z' COLLATE LATIN1_GENERAL_CI_AI
sollte tun, was Sie dort wollen, ohne dass Sie alle Zeichen auflisten müssen und Code haben, der schwer zu lesen ist