Zależy to od danych w twoich tabelach, twoich indeksów, ... Trudno powiedzieć bez możliwości porównania planów wykonania / statystyki czasu io +.
Różnica, której oczekiwałbym, to dodatkowe filtrowanie przed JOIN między dwiema tabelami. W moim przykładzie zmieniłem aktualizacje na wybory do ponownego użycia moich tabel.
Plan wykonania z „optymalizacją”
Plan wykonania
Wyraźnie widać, że ma miejsce operacja filtrowania, w moich danych testowych nie ma zapisów, które zostały odfiltrowane, w wyniku czego nie wprowadzono żadnych ulepszeń.
Plan wykonania bez „optymalizacji”
Plan wykonania
Filtr zniknął, co oznacza, że będziemy musieli polegać na sprzężeniu, aby odfiltrować niepotrzebne rekordy.
Inny powód (powody)
Innym powodem / konsekwencją zmiany zapytania może być fakt, że podczas zmiany zapytania utworzono nowy plan wykonania, który okazuje się być szybszy. Przykładem tego jest silnik wybierający innego operatora Join, ale to tylko zgadywanie w tym momencie.
EDYTOWAĆ:
Wyjaśnienie po uzyskaniu dwóch planów zapytań:
Zapytanie odczytuje z dużego stołu 550 mln wierszy i odfiltrowuje je.
Oznacza to, że predykat wykonuje większość filtrowania, a nie predykat seek. W rezultacie dane są odczytywane, ale znacznie mniej zwracane.
Zmuszenie serwera sql do użycia innego indeksu (planu zapytań) / dodanie indeksu może rozwiązać ten problem.
Dlaczego więc zapytanie optymalizacyjne nie ma tego samego problemu?
Ponieważ używany jest inny plan zapytań, ze skanem zamiast wyszukiwania.
Bez szukania, ale zwracanie do pracy tylko 4M wierszy.
Następna różnica
Pomijając różnicę aktualizacji (nic nie jest aktualizowane w zoptymalizowanym zapytaniu) dopasowanie zoptymalizowane jest używane w zoptymalizowanym zapytaniu:
Zamiast łączenia zagnieżdżonego w pętli w niezoptymalizowanym:
Pętla zagnieżdżona jest najlepsza, gdy jeden stół jest mały, a drugi duży. Ponieważ oba są zbliżone do tego samego rozmiaru, argumentowałbym, że dopasowanie skrótu jest lepszym wyborem w tym przypadku.
Przegląd
Zoptymalizowane zapytanie
Plan zoptymalizowanego zapytania ma paralelizm, wykorzystuje sprzężenie z dopasowaniem mieszającym i wymaga mniejszego resztkowego filtrowania we / wy. Wykorzystuje również mapę bitową, aby wyeliminować kluczowe wartości, które nie mogą wytworzyć żadnych wierszy łączenia. (Również nic nie jest aktualizowane)
Non-zoptymalizowane zapytania
Plan nieoptymalizowanym kwerendy ma parallellism, wykorzystuje Łączenie zagnieżdżone, i musi zrobić resztkowego IO filtrowanie na 550m rekordów. (Trwa także aktualizacja)
Co możesz zrobić, aby poprawić niezoptymalizowane zapytanie?
Zmienianie indeksu na imię i nazwisko na liście kluczowych kolumn:
CREATE INDEX IX_largeTableOfPeople_birth_date_first_name_last_name na dbo.largeTableOfPeople (data urodzenia, imię, nazwisko) obejmują (id)
Ale ze względu na użycie funkcji i dużą tabelę może to nie być optymalne rozwiązanie.
- Aktualizuję statystyki, używając rekompilacji, aby uzyskać lepszy plan.
- Dodanie OPCJI
(HASH JOIN, MERGE JOIN)
do zapytania
- ...
Dane testowe + wykorzystane zapytania
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
powinien robić, co chcesz, bez konieczności wymieniania wszystkich znaków i trudnego do odczytania kodu