Mam zapytanie, w którym użycie select *
nie tylko znacznie mniej odczytów, ale także zużywa znacznie mniej czasu procesora niż użycie select c.Foo
.
To jest zapytanie:
select top 1000 c.ID
from ATable a
join BTable b on b.OrderKey = a.OrderKey and b.ClientId = a.ClientId
join CTable c on c.OrderId = b.OrderId and c.ShipKey = a.ShipKey
where (a.NextAnalysisDate is null or a.NextAnalysisDate < @dateCutOff)
and b.IsVoided = 0
and c.ComplianceStatus in (3, 5)
and c.ShipmentStatus in (1, 5, 6)
order by a.LastAnalyzedDate
To zakończyło się 2 473 658 odczytami logicznymi, głównie w Tabeli B. Użyło 26 562 CPU i miało czas trwania 7 965.
Oto wygenerowany plan zapytań:
Na PasteThePlan: https://www.brentozar.com/pastetheplan/?id=BJAp2mQIQ
Kiedy zmienię c.ID
na *
, zapytanie zakończyło się 107 049 logicznymi odczytami, dość równomiernie rozłożonymi między wszystkimi trzema tabelami. Używał 4266 procesorów i miał czas trwania 1 147.
Oto wygenerowany plan zapytań:
Na PasteThePlan: https://www.brentozar.com/pastetheplan/?id=SyZYn7QUQ
Próbowałem użyć wskazówek do zapytania zasugerowanych przez Joe Obbisha z następującymi wynikami:
select c.ID
bez podpowiedzi: https://www.brentozar.com/pastetheplan/?id=SJfBdOELm
select c.ID
z podpowiedzi: https://www.brentozar.com/pastetheplan/ ? id = B1W ___ N87
select *
bez podpowiedzi: https://www.brentozar.com/pastetheplan/?id=HJ6qddEIm
select *
z podpowiedź: https://www.brentozar.com/pastetheplan/?id=rJhhudNIQ
Użycie OPTION(LOOP JOIN)
podpowiedzi z select c.ID
drastycznie zmniejszyło liczbę odczytów w porównaniu z wersją bez podpowiedzi, ale nadal daje około 4x liczbę odczytów select *
zapytania bez żadnych podpowiedzi. Dodanie OPTION(RECOMPILE, HASH JOIN)
do select *
zapytania spowodowało, że działało znacznie gorzej niż cokolwiek innego, co próbowałem.
Po zaktualizowaniu danych statystycznych na temat tabel i ich indeksów przy użyciu WITH FULLSCAN
The select c.ID
kwerenda działa znacznie szybciej:
select c.ID
przed aktualizacją: https://www.brentozar.com/pastetheplan/?id=SkiYoOEUm
select *
przed aktualizacją: https://www.brentozar.com/ pastetheplan /? id = ryrvodEUX
select c.ID
po aktualizacji: https://www.brentozar.com/pastetheplan/?id=B1MRoO487
select *
po aktualizacji: https://www.brentozar.com/pastetheplan/?id=Hk7si_V8m
select *
nadal przewyższa select c.ID
pod względem całkowitego czasu trwania i łącznej liczby odczytów ( select *
ma około połowy odczytów), ale zużywa więcej procesora. Ogólnie rzecz biorąc, są znacznie bliżej niż przed aktualizacją, jednak plany wciąż się różnią.
To samo zachowanie obserwuje się w 2016 r. W trybie zgodności w 2014 r. Iw 2014 r. Co może tłumaczyć rozbieżność między tymi dwoma planami? Czy to możliwe, że „poprawne” indeksy nie zostały utworzone? Czy statystyki mogą być nieco nieaktualne?
Próbowałem przenieść predykaty do ON
części złączenia na wiele sposobów, ale plan zapytań jest za każdym razem taki sam.
Po przebudowie indeksu
Odbudowałem wszystkie indeksy z trzech tabel biorących udział w zapytaniu. c.ID
nadal wykonuje najwięcej odczytów (ponad dwa razy więcej *
), ale użycie procesora to około połowa *
wersji. c.ID
Wersja rozlane również w tempdb na sortowania ATable
:
c.ID
: https://www.brentozar.com/pastetheplan/?id=HyHIeDO87
*
: https://www.brentozar.com/pastetheplan/?id=rJ4deDOIQ
Próbowałem też zmusić go do działania bez równoległości, co dało mi najlepiej działające zapytanie: https://www.brentozar.com/pastetheplan/?id=SJn9-vuLX
Zauważam, że liczba operacji operatorów PO wielkim wyszukiwaniu indeksu, który wykonuje zlecenie, wykonywana była tylko 1000 razy w wersji jednowątkowej, ale znacznie więcej w wersji równoległej, między 2622 a 4315 wykonaniami różnych operatorów.
select c.ID
zapytanie było znacznie szybsze, ale nadal wykonuje dodatkową pracę, którą wykonujeselect *
zapytanie bez podpowiedzi.Niewłaściwe statystyki mogą z pewnością spowodować, że optymalizator wybierze złą metodę wyszukiwania danych. Czy próbowałeś robić
UPDATE STATISTICS ... WITH FULLSCAN
czyli pełnyREBUILD
na indeksie? Spróbuj i sprawdź, czy to pomoże.AKTUALIZACJA
Według aktualizacji PO:
Więc teraz, jeśli jedyną podjętą czynnością było
UPDATE STATISTICS
, to spróbuj zrobić indeksREBUILD
(nieREORGANIZE
), jak widziałem, że pomoc w szacunkowym zliczaniu wierszy, w których obaUPDATE STATISTICS
i indeksREORGANIZE
nie.źródło
a) przepisz każdą tabelę jako podkwerendę, przestrzegając następujących zasad:
b) WYBIERZ - umieść najpierw kolumny łączące
c) POTWIERDZAJ - przejdź do odpowiednich podkwerend
d) ORDER BY - przejdź do ich odpowiednie podkwerendy, posortuj JOIN COLUMNS FIRST
e) Dodaj zapytanie otoki dla końcowego sortowania i wybierz.
Chodzi o to, aby wstępnie posortować kolumny łączenia wewnątrz każdego podselekcji, umieszczając kolumny łączenia na pierwszym miejscu na każdej liście wyboru.
Oto, co mam na myśli ....
źródło
ORDER BY
jest nieprawidłowy w podzapytaniu bez TOP, FORXML itp. Próbowałem bezORDER BY
klauzul, ale był to ten sam plan.