Wydajność a = 0 i b = 0 i… z = 0 vs a + b + c + d = 0

20

To proste pytanie, na które nie mogę znaleźć odpowiedzi.

Jeśli chodzi o wydajność, jeśli mam WHEREklauzulę, na przykład: a=0 and b=0 and ... z=0Czy uzyskałbym jakąkolwiek wydajność, jeśli zastąpiłbym ten warunek a+b+...+z=0?

Innymi słowy, czy jest jakikolwiek wzrost wydajności poprzez zastąpienie następujących

Select * 
From MyTable 
Where A=0 and B=0 and C=0 and D=0...

Z

Select * 
From MyTable 
Where A+B+C+D=0...

Wiem, że może zależeć od indeksów, ale w tym celu powiedzmy, że nie istnieją żadne indeksy. Czy operator arytmetyczny (+) działa lepiej niż operator logiczny „LUB” lub „AND”?

Mam wrażenie, że dodatek działa lepiej niż wiele warunków z AND lub OR.

Wyniki testów

W tabeli 4,2 miliona wierszy

Zwracane wiersze Gdzie A = 0 B = 0 i C = 0 -> 351748 Rzędy

Dodawanie (A + B + C = 0) zajęło 5 sekund, podczas gdy warunki logiczne A = 0 i B = 0 i C = 0 zajęły 11 sekund.

Z drugiej strony

Zwracane wiersze Gdzie A <> 0 B <> 0 lub C <> 0 -> 3829750 Wiersze 58 sekund

Zwracane wiersze Gdzie F65 + F67 + f64 <> 0 -> 3829750 Rzędy 57 sekund

W przypadku RNO wydaje się, że nie ma znaczącej różnicy.

Zgadzam się z GBB:

Jeśli A wynosi -1, a B wynosi 1, A + B = 0, ale A = 0, a B = 0 to fałsz

oraz z AMtwo:

ABS (A) + ABS (B) + ABS (C) + ABS (D) ... Nawet jeśli oczekujesz tylko wartości dodatnich, jeśli kolumna akceptuje wartości ujemne, powinieneś założyć, że możesz je spotkać

Wyniki są bardzo imponujące, jak myślałem, wydaje się, że dodawanie jest znacznie szybsze niż operatory logiczne.

A = Float, B = Pieniądze, a C = Float. Użyte zapytanie jest takie, jak pokazano. W moim przypadku wszystkie są liczbami dodatnimi. Brak indeksów. Moim zdaniem logiczne jest, że dodawanie byłoby szybsze niż warunki logiczne!

JohnG
źródło
Czy to boolean? Ile kolumn mówisz o 4 (w przykładach) lub 26 (w tytule)? To robi różnicę. Jaka wersja SQL Server? Gdzie gra FLOAT i PIENIĄDZE? Ile rzędów zakładamy? To pytanie ma wiele czynników.
Evan Carroll
@Evan Carroll Nie są to liczby logiczne, to liczby nieindeksowane (int, float, money itp.). Niezależnie od wersji SQL (SQL2012 i nowsze), liczby wierszy lub kolumn, pytanie polegało na ustaleniu, który operator działa lepiej - operatory logiczne w porównaniu z operatorami arytmetycznymi. Jak widać, Max Vernon doskonale demonstruje teorię na swoich przykładach.
JohnG,

Odpowiedzi:

46

W swoim pytaniu szczegółowo opisujesz niektóre przygotowane testy, w których „udowadniasz”, że opcja dodawania jest szybsza niż porównywanie odrębnych kolumn. Podejrzewam, że twoja metodologia testów może być wadliwa na kilka sposobów, o czym wspominali @gbn i @srutzky.

Po pierwsze, musisz upewnić się, że nie testujesz SQL Server Management Studio (lub innego używanego klienta). Na przykład, jeśli korzystasz SELECT *z tabeli zawierającej 3 miliony wierszy, najczęściej testujesz zdolność SSMS do pobierania wierszy z SQL Server i renderowania ich na ekranie. O wiele lepiej jest użyć czegoś takiego, SELECT COUNT(1)co neguje potrzebę ciągnięcia milionów wierszy w sieci i renderowania ich na ekranie.

Po drugie, musisz pamiętać o pamięci podręcznej danych programu SQL Server. Zazwyczaj testujemy szybkość odczytu danych z pamięci i przetwarzania tych danych z zimnej pamięci podręcznej (tzn. Bufory SQL Server są puste). Czasami sensowne jest przeprowadzanie wszystkich testów przy użyciu ciepłej pamięci podręcznej, ale należy do nich podchodzić wyraźnie.

W przypadku testu pamięci podręcznej na zimno należy uruchomić CHECKPOINTi DBCC DROPCLEANBUFFERSprzed każdym uruchomieniem testu.

Dla testu, o który pytałeś w swoim pytaniu, stworzyłem następujące łóżko testowe:

IF COALESCE(OBJECT_ID('tempdb..#SomeTest'), 0) <> 0
BEGIN
    DROP TABLE #SomeTest;
END
CREATE TABLE #SomeTest
(
    TestID INT NOT NULL
        PRIMARY KEY 
        IDENTITY(1,1)
    , A INT NOT NULL
    , B FLOAT NOT NULL
    , C MONEY NOT NULL
    , D BIGINT NOT NULL
);

INSERT INTO #SomeTest (A, B, C, D)
SELECT o1.object_id, o2.object_id, o3.object_id, o4.object_id
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
    , sys.objects o4;

SELECT COUNT(1) 
FROM #SomeTest;

Zwraca liczbę 260 144 641 na moim komputerze.

Aby przetestować metodę „dodawania”, uruchamiam:

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE (st.A + st.B + st.C + st.D) = 0;
GO
SET STATISTICS IO, TIME OFF;

Karta wiadomości pokazuje:

Tabela „#SomeTest”. Liczba skanów 3, odczyty logiczne 1322661, odczyty fizyczne 0, odczyty odczytu z wyprzedzeniem 1313877, odczyty logiczne odczytywania 0, odczyty fizyczne odczytywania 0, odczyty odczytywania odczytywania 0.

Czasy wykonania programu SQL Server: czas procesora = 49047 ms, czas, który upłynął = 173451 ms.

Dla testu „kolumn dyskretnych”:

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE st.A = 0
    AND st.B = 0
    AND st.C = 0
    AND st.D = 0;
GO

SET STATISTICS IO, TIME OFF;

ponownie z zakładki wiadomości:

Tabela „#SomeTest”. Liczba skanów 3, logiczne odczyty 1322661, fizyczne odczyty 0, odczyt z wyprzedzeniem 1322661, logiczne odczyty 0, lob fizyczne odczyty 0, lob odczyty 0.

Czasy wykonania programu SQL Server: czas procesora = 8938 ms, czas, który upłynął = 162581 ms.

Z powyższych statystyk widać drugi wariant, z dyskretnymi kolumnami w porównaniu do 0, czas, który upłynął, jest o około 10 sekund krótszy, a czas procesora jest około 6 razy krótszy. Długie czasy trwania powyższych testów wynikają głównie z odczytu wielu wierszy z dysku. Jeśli zmniejszysz liczbę wierszy do 3 milionów, proporcje pozostaną mniej więcej takie same, ale czasy, które upłynęły, zauważalnie spadają, ponieważ dyskowe operacje we / wy mają znacznie mniejszy wpływ.

Za pomocą metody „dodawania”:

Tabela „#SomeTest”. Liczba skanów 3, logiczne odczyty 15255, fizyczne odczyty 0, odczyt z wyprzedzeniem 0, lob logiczne odczyty 0, lob fizyczne odczyty 0, lob odczyty z wyprzedzeniem 0.

Czasy wykonania programu SQL Server: czas procesora = 499 ms, czas, który upłynął = 256 ms.

Dzięki metodzie „dyskretnych kolumn”:

Tabela „#SomeTest”. Liczba skanów 3, logiczne odczyty 15255, fizyczne odczyty 0, odczyt z wyprzedzeniem 0, lob logiczne odczyty 0, lob fizyczne odczyty 0, lob odczyty z wyprzedzeniem 0.

Czasy wykonania programu SQL Server: czas procesora = 94 ms, czas, który upłynął = 53 ms.

Co zrobi naprawdę dużą różnicę w tym teście? Odpowiedni indeks, taki jak:

CREATE INDEX IX_SomeTest ON #SomeTest(A, B, C, D);

Metoda „dodawania”:

Tabela „#SomeTest”. Liczba skanów 3, logiczne odczyty 14235, fizyczne odczyty 0, odczytywanie z wyprzedzeniem 0, lob logiczne odczyty 0, lob fizyczne odczyty 0, lob odczyty z wyprzedzeniem 0.

Czasy wykonania SQL Server: czas procesora = 546 ms, czas, który upłynął = 314 ms.

Metoda „dyskretnych kolumn”:

Tabela „#SomeTest”. Liczba skanów 1, logiczne odczyty 3, fizyczne odczyty 0, odczyt z wyprzedzeniem 0, lob logiczne odczyty 0, lob fizyczne odczyty 0, lob odczyty z wyprzedzeniem 0.

Czasy wykonania programu SQL Server: czas procesora = 0 ms, czas, który upłynął = 0 ms.

Plan wykonania każdego zapytania (z powyższym indeksem na miejscu) jest dość wymowny.

Metoda „dodawania”, która musi wykonać skanowanie całego indeksu:

wprowadź opis zdjęcia tutaj

a metoda „dyskretnych kolumn”, która może wyszukiwać do pierwszego wiersza indeksu, w którym wiodąca kolumna indeksu A, wynosi zero:

wprowadź opis zdjęcia tutaj

Max Vernon
źródło
24

Załóżmy, że masz indeks A, B, C i D. Można go również przefiltrować.

Bardziej prawdopodobne jest użycie indeksu niż dodania.

Where A=0 and B=0 and C=0 and D=0

Z innych wiadomości, jeśli A to -1, a B to 1, A+B=0to prawda, ale A=0 and B=0fałsz.

gbn
źródło
7

(Uwaga: odpowiedź została udzielona przed odnotowaniem jakiegokolwiek testu w pytaniu: tekst pytania zakończył się tuż nad sekcją Wyniki testu ).

Sądzę, że osobne ANDwarunki byłyby preferowane, ponieważ optymalizator miałby większe prawdopodobieństwo zwarcia operacji, jeśli jeden z nich nie jest równy 0, bez konieczności wykonywania obliczeń w pierwszej kolejności.

Mimo to, ponieważ jest to kwestia wydajności, należy najpierw skonfigurować test określić odpowiedź na swoim sprzęcie. Zgłoś te wyniki, pokazując swój kod testowy, i poproś innych o sprawdzenie go, aby upewnić się, że był to dobry test. Mogą istnieć inne czynniki warte rozważenia, o których nie pomyślałeś.

Solomon Rutzky
źródło
3

Jakieś ogólne rozumowanie, jeśli nie masz pod ręką żadnych indeksów, nie sądzę, żeby miało to duże znaczenie, które z dwóch wybranych przez ciebie rozwiązań, oba będą źle działać. Jeśli z drugiej strony masz indeks na jednej lub więcej kolumn w predykacie, pierwsza z nich prawdopodobnie będzie działać lepiej niż druga, ponieważ druga prawdopodobnie nie będzie w stanie korzystać z indeksów.

Disjunctions (OR) ogólnie działa gorzej niż koniunkcje (AND), ale nawet jeśli masz zapytanie z disjunkctions, postawię moje pieniądze na pierwszym.

Lennart
źródło
2

To proste pytanie

Nie, nie jest. To (rodzaj) pytanie jest tym, co trapi wielu DBA i programistów każdego dnia i jest prawie banalne.

na który nie mogę znaleźć odpowiedzi.

Tak nie będziesz. Przynajmniej nie ogólna odpowiedź. Przede wszystkim będzie to zależeć w ogromnym stopniu od używanego RDBMS (OK, używasz , ale nadal). Może nawet ulec zmianie po przejściu z jednej wersji RDBMS do następnej.

Następnie może zależeć od dowolnej liczby innych drobnych szczegółów, na przykład, jak twoja baza danych przechowuje dane, jeśli masz podselekcje / złączenia, które mylą problem optymalizatora planu itp. Optymalizator może dać ci różne plany wykonania w zależności na ile masz wierszy ...

Wykonanie testu w świecie rzeczywistym jest zwykle jedynym przydatnym sposobem rozwiązania takich pytań. Ponadto wszelkie zyski uzyskane dzięki takim „tajemnym” optymalizacjom są zwykle dziesięciokrotnie pochłaniane przez sprytny wybór indeksów, więc nie zawracałbym sobie głowy spędzaniem nad nimi dużo czasu, zanim użycie indeksów zostanie naprawdę wykluczone.

AnoE
źródło
0

Może to być oczywiste, ale jeśli kolumny są INT, wówczas a+b+cmoże być równe zero, nawet jeśli żadna z nich nie jest równa zero. Testujesz dwie różne rzeczy!

Ross Presser
źródło
Właśnie zdałem sobie sprawę, że @gbn wspomniał o tym w swojej odpowiedzi.
Ross Presser