2009-09-08 5 views
10

Wybieram niektóre wiersze z funkcji wycenione w tabeli, ale znalazłem niewytłumaczalną ogromną różnicę wydajności poprzez umieszczenie SELECT TOP w zapytaniu.SQL ogromna różnica wydajności przy użyciu SELECT TOP x, nawet gdy x jest znacznie wyższa niż wybrane wiersze

SELECT col1, col2, col3 etc 
FROM  dbo.some_table_function 
WHERE col1 = @parameter 
--ORDER BY col1 

trwa do 5 lub 6 minut do zakończenia.

Jednakże

SELECT TOP 6000 col1, col2, col3 etc 
FROM  dbo.some_table_function 
WHERE col1 = @parameter 
--ORDER BY col1 

zakończona w około 4 lub 5 sekund.

Nie zdziwiłoby mnie to, gdyby zwrócony zestaw danych był ogromny, , ale konkretne zapytanie dotyczyło ~ 5000 wierszy z 200 000.

W obu przypadkach cała tabela jest przetwarzana, ponieważ SQL Server kontynuuje wyszukiwanie 6000 wierszy, do których nigdy nie dotrze. Skąd ta ogromna różnica? Czy ma to jakiś związek ze sposobem, w jaki SQL Server przydziela przestrzeń w oczekiwaniu na rozmiar zestawu wyników (TOP 6000, co daje mu niskie wymagania, które łatwiej jest przypisać w pamięci)? Czy ktoś jeszcze był świadkiem czegoś takiego?

Dzięki

+0

Czy obejrzałeś plany zapytań? Czy istnieje różnica? –

+2

Ciekawe, co się stanie z wydajnością, jeśli powiesz WYBIERZ 100 PERCENTÓW? –

+0

Zgaduję, że masz jakieś statystyki, które wyrzucają optymalizator zapytania z keltera. Optymalizator może na przykład zdecydować się na użycie skanowania tabeli zamiast szukania indeksu, jeśli uważa, że ​​w tabeli jest bardzo mało wierszy. Dlaczego nie ma to wpływu na zapytanie TOP, którego nie znam, ale sprawdzam plany wykonania. Pokazują ci to, co robi serwer, a to wyjaśnia, dlaczego jest wolny. Wyświetli ona również szacunkową i faktyczną liczbę wierszy. Jeśli niektóre szacunki są zbyt daleko, zaktualizuj statystyki i spróbuj ponownie. :) –

Odpowiedz

6

Funkcje wycenione w tabeli mogą mieć nieliniowy czas wykonania.

Rozważmy odpowiednik funkcji dla tego zapytania:

SELECT (
     SELECT SUM(mi.value) 
     FROM mytable mi 
     WHERE mi.id <= mo.id 
     ) 
FROM mytable mo 
ORDER BY 
     mo.value 

To zapytanie (który oblicza biegu SUM) jest szybki na początku i powoli na koniec, ponieważ w każdym rzędzie od mo należy zsumować wszystkie poprzedzające wartości, które wymagają przewijania wierszy.

Czas do obliczenia SUM dla każdego wiersza wzrasta wraz ze wzrostem liczby wierszy.

Jeśli zrobisz mytable wystarczająco dużą (powiedzmy 100,000 wierszy, jak w twoim przykładzie) i uruchom to zapytanie, zobaczysz, że zajmuje to dużo czasu.

Jeśli jednak zastosujesz TOP 5000 do tego zapytania, zobaczysz, że kończy się znacznie szybciej niż 1/20 czasu potrzebnego na pełną tabelę.

Najprawdopodobniej coś podobnego dzieje się również w twoim przypadku.

Aby powiedzieć coś zdecydowanie, potrzebuję zobaczyć definicję funkcji.

Aktualizacja:

SQL Server może wypchnąć predykaty do funkcji.

Na przykład, po prostu stworzył ten TVF:

CREATE FUNCTION fn_test() 
RETURNS TABLE 
AS 
RETURN (
     SELECT * 
     FROM master 
     ); 

te pytania:

SELECT * 
FROM fn_test() 
WHERE name = @name 

SELECT TOP 1000 * 
FROM fn_test() 
WHERE name = @name 

dawać różne plany wykonania (pierwszy z nich używa klastrowego skanowanie, drugi wykorzystuje indeks szukać z TOP)

+0

"W tym przypadku nie dotyczy Fraid. Punktem mojego zapytania jest to, że wiersze _same_ są zwracane bez względu na to, czy klauzula TOP była używana, czy nie (TOP 6000 jest większy niż zestaw wyników). W związku z tym nie może być to związane z obliczaniem samych tych rzędów. – Ray

+0

'@ Arj': czy mógłbyś opublikować swoją definicję funkcji? – Quassnoi

+0

@Quassnoi: wbudowany TVF jest po prostu makrem. – gbn

1

To niekoniecznie prawda, że ​​cały stół jest przetwarzany, jeśli col1 ma indeks.

Optymalizacja SQL określa, czy użyć indeksu. Być może twój "TOP" zmusza go do korzystania z indeksu.

Jeśli używasz MSSQL Query Analyzer (Nazwa ucieka mi), naciśnij Ctrl-K. Spowoduje to wyświetlenie planu wykonania zapytania zamiast jego wykonania. Przypuszczam, że pokazanie ikon wskazuje na użycie IO/CPU.

Założę się, że jeden używa wyszukiwania indeksu, a drugi nie.

Jeśli masz ogólne klienta: SET SHOWPLAN_ALL ON; GO wybierz ...; iść

zobaczyć http://msdn.microsoft.com/en-us/library/ms187735.aspx o szczegóły.

+0

Tak, teraz patrzę na plan. Chociaż zmieniłem zapytanie o publikację. W rzeczywistości robi SELECT *. Nie rozumiem, w jaki sposób użycie GÓRY zachęci do korzystania z indeksu? – Ray

+0

Optymalizator SQL podejmie decyzję, czy użyć indeksu. Zrobiłem kwerendy, gdzie klauzula where powoduje "punkt krytyczny", w którym optymalizator decyduje się na pełne skanowanie tabeli, zamiast używać indeksu. – ericp

1

Być może używasz tutaj czegoś tak prostego jak buforowanie - być może (z jakiegokolwiek powodu) zapytanie "TOP" jest buforowane? Używając indeksu, którego nie ma drugi?

W każdym razie najlepszym sposobem na zaspokojenie swojej ciekawości jest zbadanie pełnego planu wykonania dla obu zapytań. Możesz to zrobić bezpośrednio w SQL Management Console i powie Ci dokładnie, jakie operacje są wykonywane i jak długo każdy z nich jest przewidywany.

Wszystkie implementacje SQL są dziwaczne na swój własny sposób - SQL Server nie jest wyjątkiem. Taki rodzaj "whaaaaa ?!" chwile są dość powszechne. ; ^)

3

Twój TOP ma ORDER BY, więc jest to po prostu taki sam jak SET ROWCOUNT 6000 pierwszy. ORDER BY wymagałby najpierw oceny wszystkich wierszy, a to zajmie dużo więcej czasu.

Jeśli dbo.some_table_function jest tabelą wbudowaną o wartości udf, to jest to po prostu makro, które jest rozszerzone, więc zwraca pierwsze 6000 wierszy, jak wspomniano w żadnej konkretnej kolejności.

Jeśli UDF jest wielo cenione, to jest to czarna skrzynka i zawsze będzie ciągnąć w pełnym zbiorze przed filtrowaniem. Nie sądzę, żeby tak się działo.

niezwiązanych bezpośrednio, ale another SO question on TVFs

1

myślę sugestia Quassnois' wydaje się bardzo prawdopodobne. Dodając TOP 6000, domyślnie dajesz optymalizatorowi wskazówkę, że zostanie zwrócony dość mały podzbiór 200 000 wierszy. Optymalizator następnie korzysta z wyszukiwania indeksu zamiast sklasyfikowanego skanowania indeksu lub tabeli.

Kolejne możliwe wyjaśnienie może buforować, jak sugeruje Jim Davis. Jest to dość łatwe do wykluczenia przez ponowne uruchomienie zapytań. Spróbuj najpierw uruchomić model z TOP 6000.

2

Miałem ten sam problem, proste zapytanie łączące pięć tabel zwracających 1000 wierszy zajęło dwie minuty. Kiedy dodałem do niego "TOP 10000", zakończyło się to w mniej niż sekundę. Okazało się, że indeks klastrowy w jednej z tablic był mocno rozdrobniony.

Po odbudowaniu indeksu zapytanie kończy się w mniej niż sekundę.