Zauważam, że gdy dochodzi do rozlania zdarzeń tempdb (powodujących powolne zapytania), to często szacunki wierszy są dalekie od konkretnego łączenia. Widziałem zdarzenia rozlewania występujące w połączeniach scalania i mieszania, które często zwiększają czas działania 3x do 10x. To pytanie dotyczy sposobu poprawy oszacowań wierszy przy założeniu, że zmniejszy to ryzyko wystąpienia wycieków.
Rzeczywista liczba rzędów 40 tys.
W przypadku tego zapytania plan pokazuje złe oszacowanie wiersza (11,3 wiersza):
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
W przypadku tego zapytania plan pokazuje dobre oszacowanie wiersza (56 tys. Wierszy):
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile);
Czy można dodać statystyki lub wskazówki, aby poprawić szacunki wierszy dla pierwszego przypadku? Próbowałem dodać statystyki z określonymi wartościami filtrów (właściwość = 2840), ale albo nie udało się uzyskać poprawnej kombinacji, albo być może jest ona ignorowana, ponieważ ObjectId jest nieznany w czasie kompilacji i może wybierać średnią dla wszystkich ObjectIds.
Czy jest jakiś tryb, w którym najpierw wykonałby zapytanie z sondy, a następnie użył go do ustalenia szacunków wiersza, czy też musi latać na ślepo?
Ta konkretna właściwość ma wiele wartości (40k) na kilku obiektach i zero na zdecydowanej większości. Byłbym zadowolony z podpowiedzi, w której można określić maksymalną oczekiwaną liczbę wierszy dla danego sprzężenia. Jest to ogólnie problem nawiedzający, ponieważ niektóre parametry mogą być określane dynamicznie jako część złączenia lub byłyby lepiej umieszczone w widoku (brak obsługi zmiennych).
Czy są jakieś parametry, które można dostosować, aby zminimalizować ryzyko wycieków do tempdb (np. Minimalna pamięć na zapytanie)? Solidny plan nie miał wpływu na oszacowanie.
Edytuj 2013.11.06 : Odpowiedź na komentarze i dodatkowe informacje:
Oto obrazy planu zapytania. Ostrzeżenia dotyczą predykatu liczności / poszukiwania za pomocą funkcji convert ():
Według komentarza @Aarona Bertranda próbowałem zastąpić metodę convert () jako test:
create table Oav.SeekObject (
LookupId bigint not null primary key,
ObjectId bigint not null
);
insert into Oav.SeekObject (
LookupId, ObjectId
) VALUES (
1, 3540233
)
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.SeekObject
where LookupId = 1)
and PropertyId = 2840
option (recompile);
Jako dziwny, ale udany punkt zainteresowania, pozwolił również na zwarcie wyszukiwania:
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.ValueArray
where PropertyId = 2840
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Oba z nich zawierają poprawne wyszukiwanie klucza, ale tylko te pierwsze zawierają „wynik” ObjectId. Wydaje mi się, że to wskazuje, że drugim jest zwarcie?
Czy ktoś może zweryfikować, czy kiedykolwiek wykonywane są sondy jednorzędowe, aby pomóc w oszacowaniu rzędu? Wydaje się niewłaściwe ograniczanie optymalizacji do szacunków histogramu tylko wtedy, gdy jednorzędowe wyszukiwanie PK może znacznie poprawić dokładność wyszukiwania w histogramie (szczególnie jeśli istnieje potencjał rozlania lub historia). Gdy w prawdziwym zapytaniu występuje 10 takich podłączy, idealnie byłyby one realizowane równolegle.
Na marginesie, ponieważ sql_variant przechowuje swój typ podstawowy (SQL_VARIANT_PROPERTY = BaseType) w samym polu, oczekiwałbym, że funkcja convert () będzie prawie bezkosztowa, pod warunkiem, że jest „bezpośrednio” konwertowalna (np. Nie ciąg znaków na dziesiętne, ale raczej int int, a może int to bigint). Ponieważ nie jest to znane w czasie kompilacji, ale może być znane użytkownikowi, być może funkcja „AssumeType (typ, ...)” dla zmiennych_sql pozwoliłaby na ich bardziej przejrzyste traktowanie.
źródło
declare @a bigint =
tego, co zrobiłeś, wydaje mi się naturalnym rozwiązaniem, dlaczego jest to niedopuszczalne?CONVERT()
w kolumnach, a następnie do ich połączenia. To z pewnością nie jest wydajne w większości przypadków. W tym konkretnym przypadku należy przekonwertować tylko jedną wartość, więc prawdopodobnie nie jest to problem, ale jakie indeksy masz na stole? Projekty EAV zwykle działają dobrze, tylko przy odpowiednim indeksowaniu (co oznacza wiele indeksów w zwykle wąskich tabelach).Odpowiedzi:
Nie będę komentował wycieków, tempdb ani podpowiedzi, ponieważ zapytanie wydaje się dość proste, aby wymagało tyle uwagi. Myślę, że optymalizator SQL-Server dobrze sobie poradzi, jeśli istnieją indeksy odpowiednie dla zapytania.
Podział na dwa zapytania jest dobry, ponieważ pokazuje, jakie indeksy będą przydatne. Pierwsza część:
potrzebuje indeksu
(PropertyId, ObjectId, Sequence)
uwzględniającegoValue
. Sprawiłbym,UNIQUE
żeby było bezpiecznie. Zapytanie i tak wygenerowałoby błąd podczas działania, gdyby zwrócono więcej niż jeden wiersz, więc dobrze jest wcześniej upewnić się, że tak się nie stanie, dzięki unikalnemu indeksowi:Druga część zapytania:
potrzebuje indeksu
(PropertyId, ObjectId)
obejmującegoValue
:Jeśli wydajność nie ulegnie poprawie lub indeksy te nie zostaną użyte lub nadal będą występować różnice w pojawiających się szacunkach wierszy, konieczne będzie dalsze przyjrzenie się temu zapytaniu.
W takim przypadku konwersje (wymagane z projektu EAV i przechowywania różnych typów danych w tych samych kolumnach) są prawdopodobną przyczyną, a twoje rozwiązanie podziału (jak komentarz @Aron Bertrand i @Paul White) wydaje się naturalne i droga do przebycia. Przeprojektowanie, aby mieć różne typy danych w odpowiednich kolumnach, może być innym.
źródło
Jako częściowa odpowiedź na jednoznaczne pytanie dotyczące poprawy statystyk ...
Zauważ, że szacunki rzędu nawet dla osobnego zepsutego przypadku są nadal wyłączone o 10X (4k vs oczekiwane 40k).
Histogram statystyczny prawdopodobnie był zbyt cienki dla tej właściwości, ponieważ jest to długa (pionowa) tabela wierszy o wielkości 3,5 mln, a ta konkretna właściwość jest bardzo rzadka.
Utwórz dodatkowe statystyki (nieco zbędne w przypadku statystyki IX) dla rzadkiej właściwości:
Pierwotni:
Po usunięciu konwersji () (właściwej):
Po usunięciu konwersji () (krótki obwód):
Prawdopodobnie jeszcze ~ 2X prawdopodobne, ponieważ> 99,9% obiektów w ogóle nie ma zdefiniowanej właściwości 2840. W rzeczywistości tylko w tym przypadku testowym właściwość istnieje tylko na 1 z 200 000 różnych obiektów tabeli wierszy 3,5 mln. To niesamowite, że jest tak blisko. Dostosowując filtr do mniejszej liczby ObjectIds,
Hmm, bez zmian ... Kopia zapasowa dodała „z pełnym skanowaniem” na końcu statystyk (być może dlatego poprzednie dwa nie działały) i tak:
Tak Tak więc w wysoce pionowej tabeli z szeroko zakrywającym IX dodanie dodatkowej filtrowanej statystyki wydaje się być dużym ulepszeniem (szczególnie w przypadku rzadkich, ale bardzo różnorodnych kombinacji klawiszy).
źródło