Dlaczego mój indeks może oszacować odpowiednią liczbę wierszy, a operator sortowania nie może?

11

Mam zapytanie, które korzysta z funkcji w predykacie, mniej więcej tak:

commentType = 'EL'
AND commentDateTime >= DATEADD(month,datediff(month,0,getdate()) - 13,0)

Mam filtrowany indeks na commentType, który ma 40 tys. Wierszy, a kiedy uruchamiam zapytanie, szacunkowa liczba wierszy dla szukania indeksu jest bardzo dokładna (około 11 tys.), Ale w następnym kroku (operator sortowania) całkowicie ignoruje statystyki i po prostu szacuje całkowitą liczbę wierszy w przefiltrowanym indeksie.

Dlaczego to się dzieje? Znam podstawy dotyczące sargability i przetestowałem dla zachowania rozsądku, zastępując dateadd rzeczywistą datą (01.01.2014) i voila ... Sortowanie zaczęło poprawnie zgadywać liczbę wierszy ...

Dlaczego tak się dzieje i jak mogę to naprawić? Nie mogę podać ustalonej daty ...

MrKudz
źródło
DATEADD(month,datediff(month,0,getdate()) - 13,0)nie ma dla mnie sensu. Co próbujesz z tym zrobić? Czy można to poprawić / uprościć?
Daniel Hutmacher
2
@Daniel To jest początek miesiąca, 13 miesięcy temu.
Aaron Bertrand
1
Edytuj również pytanie, aby odzwierciedlić wersję programu SQL Server (?), W której jesteś. Użyj do tego tagów.
Daniel Hutmacher
Czy możesz spróbować DATEADD(month, -13, DATEADD(day, 1-DATEPART(day, SYSDATETIME()))sprawdzić, czy jest jakaś różnica?
Daniel Hutmacher
Jeśli masz włączony filtr niefiltrowany (commentType, commentDate), czy działa tam lepiej? Po prostu filtrowane indeksy mogą czasem błędnie zgłaszać szacunki w różnych punktach planów. Oszacowanie wydaje się wyjściem, zgłaszając całkowitą liczbę w przefiltrowanym indeksie, ale tak naprawdę plan jest wyświetlany nieprawidłowo.
Rob Farley,

Odpowiedzi:

9

Uważam, że twoje szacunki są błędne z powodu błędu estymatora, który zamienia dwa argumenty DATEDIFF. Mówię o tym tutaj:

Obejściem tego problemu jest obliczenie pierwszego dnia 13 miesięcy temu bez użycia DATEDIFF (2008+):

DATEADD(MONTH, -13, DATEADD(DAY, 1-DATEPART(DAY,GETDATE()), CONVERT(DATE, GETDATE()));

Nie jestem pozytywny , że zajmie się szacunek (nie testowałem z filtrowanych indeksów, a nie jestem pewien, co ten rodzaj jest rzeczywiście robi i dlaczego to ma inny szacunek bez planu i / lub w pozostałej części zapytania ).

Poprawka, którą Microsoft zaleca, to użycie TF 4199, ale nie jestem pewien, czy właśnie to musisz zrobić tutaj:

Inną opcją byłoby upewnienie się, że korzystasz z najnowszej wersji SP / CU dla dowolnej wersji SQL Server, której używasz, ponieważ twierdzą, że zostało to naprawione w następującym artykule KB (choć nadal będzie to wymagało użycia TF 4199 chyba że masz rok 2014 lub lepszy):

Poprawka może zostać uzyskana w następujących wersjach:

  • 2005 SP3 CU 15 (> = 9.00.4325 I <= 9.00.4999)
  • 2005 SP4 CU 2 (> = 9.00.5259)
  • 2008 SP1 CU 13 (> = 10.00.2816.00 I <= 10.00.3999)
  • 2008 SP2 CU 3 (> = 10.00.4279.00 I <= 10.00.5499)
  • Według rozszerzenia 2008 SP3 i SP4 (> = 10.00.5500)
  • 2008 R2 CU 7 (10.50.1777.0)
  • 2008 R2 SP1 CU 3 (> = 10,50.2769,0 i <= 10,50.3999)
  • Według rozszerzenia 2008 R2 SP2 i SP3 (> = 10.50.4000)
  • Według rozszerzenia 2012, 2014, 2016 (> = 11,0)

(Następnym razem podaj wyniki SELECT @@VERSIONswojego pytania).

Zwrócę uwagę, że artykuł z bazy wiedzy mówi, że DATEDIFF może nie doceniać liczby wierszy, co jest przeciwieństwem tego, co dzieje się w twoim scenariuszu. To nie znaczy, że poprawki cię nie dotyczą; Myślę, że sformułowanie artykułu z bazy wiedzy jest niedokładne, ponieważ szacunki mogą iść w obie strony w zależności od danych i zakresu, na który patrzysz.

Mój post na blogu powyżej potwierdził, że zamiana nie występuje już w 2014 roku i później. Dla bezpieczeństwa prawdopodobnie usunęłbym DATEDIFF z predykatu i użyłbym innej metody do obliczenia początku zakresu. Nie sugeruję przekroczenia 4199 lub używania dynamicznego SQL, aby zapobiec złej zamianie.

Aaron Bertrand
źródło
Dzięki za pomoc ! Spróbowałem twojej sugestii i plan się zmienił. Tak było wcześniej: s16.postimg.org/t5j6o1yed/fix_wrong.png Tak to jest po tym, jak zmieniłem datownik na twój: postimg.org/image/5f725rj83 Przeczytam wszystkie adresy URL, które mi dałeś . Twoje zdrowie.
MrKudz