2012-07-01 12 views
12

Właśnie dostałem małe pytanie. Podczas próby uzyskania pojedynczej wartości maksymalnej tabeli. Który jest lepszy?Wydajność SQL MAX()

SELECT MAX(id) FROM myTable WHERE (whatever) 

lub

SELECT TOP 1 id FROM myTable WHERE (whatever) ORDER BY id DESC 

Używam Microsoft SQL Server 2012

+2

Czy to wypróbowałeś? Oczekuję, że będą takie same, jeśli optymalizator jest dobry. – Hogan

+1

Jeśli "id" jest automatycznym inkrementowaniem, to pytanie jest duplikatem http://stackoverflow.com/questions/590079/for-autoincrement-fields-maxid-vs-top-1-id-order-by-id-desc – Ben

+0

id oznacza dowolną kolumnę dowolnego typu. –

Odpowiedz

26

Nie będzie żadnych różnic, ponieważ możesz sprawdzić się, sprawdzając plany wykonania. Jeśli id jest indeksem klastrowym, powinieneś zobaczyć uporządkowany skany indeksu klastra; jeśli nie jest zindeksowane, nadal będzie można wyświetlić skanowanie tabeli lub sklasyfikowany indeks, ale nie zostanie on zamówiony w obu przypadkach.

Podejście TOP 1 może być przydatne, jeśli chcesz przeciągnąć inne wartości z wiersza, co jest łatwiejsze niż ciągnięcie maksimum w podzapytaniu, a następnie dołączanie. Jeśli chcesz inne wartości z rzędu, musisz dyktować, jak radzić sobie z więzami w obu przypadkach.

Po tym, istnieją pewne scenariusze, w których plan może się różnić, dlatego ważne jest, aby przetestować w zależności od tego, czy kolumna jest indeksowana i czy monotonicznie rośnie. Utworzony prostego stołu i dodaje 50000 wierszy:

CREATE TABLE dbo.x 
(
    a INT, b INT, c INT, d INT, 
    e DATETIME, f DATETIME, g DATETIME, h DATETIME 
); 
CREATE UNIQUE CLUSTERED INDEX a ON dbo.x(a); 
CREATE INDEX b ON dbo.x(b) 
CREATE INDEX e ON dbo.x(e); 
CREATE INDEX f ON dbo.x(f); 

INSERT dbo.x(a, b, c, d, e, f, g, h) 
SELECT 
    n.rn, -- ints monotonically increasing 
    n.a, -- ints in random order 
    n.rn, 
    n.a, 
    DATEADD(DAY, n.rn/100, '20100101'), -- dates monotonically increasing 
    DATEADD(DAY, -n.a % 1000, '20120101'),  -- dates in random order 
    DATEADD(DAY, n.rn/100, '20100101'), 
    DATEADD(DAY, -n.a % 1000, '20120101') 
FROM 
(
    SELECT TOP (50000) 
    (ABS(s1.[object_id]) % 10000) + 1, 
    rn = ROW_NUMBER() OVER (ORDER BY s2.[object_id]) 
    FROM sys.all_objects AS s1 
    CROSS JOIN sys.all_objects AS s2 
) AS n(a,rn); 
GO 

w systemie Spowodowało to wartości w/c od 1 do 50000, b/d między 3 a 9994 E/G z 2010-01-01 przez 2011-05-16, i f/h od 2009-04-28 do 2012-01-01.

Najpierw porównajmy indeksowane monotonicznie rosnące kolumny całkowite a i c. A ma indeksu klastrowego, c nie:

SELECT MAX(a) FROM dbo.x; 
SELECT TOP (1) a FROM dbo.x ORDER BY a DESC; 

SELECT MAX(c) FROM dbo.x; 
SELECT TOP (1) c FROM dbo.x ORDER BY c DESC; 

Wyniki:

enter image description here

Duży problem z 4. zapytania jest to, że w przeciwieństwie do MAX, to wymaga pewnego rodzaju. Tutaj 3 w porównaniu do 4:

enter image description here

enter image description here

będzie to powszechny problem we wszystkich tych odmianach zapytania: a MAX na kolumnie niezindeksowane będzie mógł świnka powrotem na klastrze indeksuj skanuj i wykonuj agregację strumieniową, podczas gdy TOP 1 musi wykonać sortowanie, które będzie droższe.

Zrobiłem test i zobaczyłem dokładnie takie same wyniki w testach b + d, e + g i f + h.

Wydaje mi się, że oprócz generowania większej ilości kodów zgodności z normami, istnieje potencjalne korzyści związane z używaniem MAX na rzecz TOP 1 w zależności od tabeli i indeksów (które mogą ulec zmianie po umieszczeniu) twój kod w produkcji). Powiedziałbym więc, że bez dodatkowych informacji preferowane jest ustawienie MAX.

(I jak powiedziałem wcześniej, TOP 1 może być naprawdę zachowanie jesteś po, jeśli wyciągając dodatkowe kolumny. Będziemy chcieli przetestować MAX + JOIN metod oraz, jeśli to Ty jesteś po.)

+0

+1 - Ale czy jakikolwiek SQL był używany do obliczania twoich wartości procentowych, czy też zapytałeś XML o plan wykonania? Byłoby miło mieć to w twojej odpowiedzi, aby przyszli czytelnicy otrzymali również wiedzę na temat tego, jak sami przesłuchać te plany. – Wayne

+1

Po prostu wyświetlałem odpowiednie wyniki z SQL Sentry Plan Explorer, darmowego narzędzia z mojej firmy. http://sqlsentry.net/ –

+0

[Dobry artykuł na temat "Top N" tutaj.] (http://sqlblog.com/blogs/paul_white/archive/2010/08/27/sorting-row-goals-and -the-top-100-problem.aspx), jeśli ktoś jest zainteresowany. Nie musi on faktycznie sortować wszystkich wierszy (wystarczy, aby śledzić "TOP 1"), ale wymaga dotacji na pamięć, inaczej niż w przypadku agregacji strumieniowej. –

5

Pierwszym z nich jest z pewnością bardziej przejrzyste w intencji.

Dla tej konkretnej kwerendy nie powinna występować znacząca różnica w wydajności (w rzeczywistości powinny być prawie identyczne, nawet jeśli wynik jest inny, jeśli w wierszu myTable nie ma wierszy). Jeśli nie masz uzasadnionego powodu do dostrojenia zapytania (np. Sprawdzonego problemu z wydajnością), zawsze wybieraj ten, który pokazuje zamiar kodu.

+3

Dodatkową zaletą jest to, że pierwsza kwerenda jest również agnostyczna dla DBMS, co oznacza, że ​​można wykonać to samo zapytanie i uruchomić ją na prawie każdym innym systemie DBMS i nadal będzie działała, podczas gdy druga kwerenda używa specyficznego dla serwera SQL 'TOP 'słowo kluczowe obsługiwane tylko przez SQL-Server. –

2

Wszystkie optymalizatory zapytań warte swojej soli powinny tworzyć plany zapytań o identycznej wydajności dla obu zapytań: jeśli istnieje indeks optymalizowanej kolumny, oba zapytania powinny z niej korzystać; jeśli nie ma indeksu, oba wygenerują pełne skanowanie tabeli.

0

Chociaż podejrzewam, że operator sortowania TOP 1 jest zawyżony w planie. Próbowałem z TOP 1, TOP 100,> i TOP 101 i wszyscy dali mi taki sam szacowany koszt podtestu, mimo że ostatni> musiałby posortować wszystkie wiersze. - Martin Smith 2 lipca o 6:53

Niezależnie od tego, czy potrzebujesz 1 wiersza, czy 100 wierszy, optymalizator musi wykonać tę samą pracę w tym przykładzie, tj. Odczytać wszystkie wiersze z tabeli (indeks klastrowy). sortuj wszystkie te wiersze (operację sortowania), ponieważ w kolumnie C nie ma indeksu. Po prostu wyświetl, który z nich jest potrzebny.

SELECT TOP (1) b FROM dbo.x ORDER BY b DESC 
option(recompile); 
SELECT TOP (100) b FROM dbo.x ORDER BY b DESC 
option(recompile); 

Spróbuj powyższego kodu, a tutaj pierwsza 1 i pierwsza 100 pokazuje koszt różnic, ponieważ w kolumnie b znajduje się indeks. Tak więc w tym przypadku nie trzeba czytać wszystkich wierszy i sortować ich, ale praca ma przejść do wskaźnika ostatniej strony. Dla jednego wiersza czytamy ostatni wiersz na ostatniej stronie liścia indeksu. TFor 100 wiersz znajdź ostatni wiersz na ostatniej stronie, a następnie rozpocznij skanowanie do tyłu, aż uzyskasz 100 wierszy.

+0

To nie jest poprawne. Przeczytaj [podany link, który wyjaśnia, jak działa sortowanie "TOP N"] (http://sqlblog.com/blogs/paul_white/archive/2010/08/27/sorting-row-goals-and-the-top -100-problem.aspx). 100 to magiczna liczba pomiędzy różnymi metodami, ale nie wygląda na to, że koszt w planie uwzględnia to, ponieważ kosztuje to samo dla 'TOP 1' vs' TOP 50000' przy uruchomieniu przeciwko danym demo Aarona.W przypadku "TOP 1" zasadniczo wystarczy śledzić maksymalną wartość, która jest taka sama, jak w przypadku "MAX", chociaż jest ona realizowana inaczej. Nie trzeba sortować wszystkich 50 000 wierszy. –

+0

Bez sortowania 50000 wierszy, w jaki sposób można wiedzieć, która wartość jest maksymalna, jeśli lista nie jest sortowana. Brak indeksu w kolumnie C. –

+0

Skanując je wszystkie i porównując każdą z wartością "TOP 1", którą widzieliście do tej pory. Nie ma potrzeby sortowania całego zestawu 50 000 wierszy. –