Przeważnie już odpowiedziałeś na to pytanie. Muszę dodać kilka kęsów:
W PostgreSQL (i innych RDBMS, które obsługują ten boolean
typ) możesz bezpośrednio użyć boolean
wyniku testu. Prześlij do integer
i SUM()
:
SUM((amount > 100)::int))
Lub użyj go w NULLIF()
wyrażeniu i COUNT()
:
COUNT(NULLIF(amount > 100, FALSE))
Lub za pomocą prostego OR NULL
:
COUNT(amount > 100 OR NULL)
Lub różne inne wyrażenia. Wydajność jest prawie identyczna . COUNT()
jest zwykle bardzo nieznacznie szybszy niż SUM()
. W przeciwieństwie do SUM()
i jak Paul już zauważył , COUNT()
nigdy nie powraca NULL
, co może być wygodniejsze. Związane z:
Od Postgres 9.4 istnieje również FILTER
klauzula . Detale:
Jest szybszy niż wszystkie powyższe o około 5-10%:
COUNT(*) FILTER (WHERE amount > 100)
Jeśli zapytanie jest tak proste jak przypadek testowy, zawiera tylko jedną liczbę i nic więcej, możesz przepisać:
SELECT count(*) FROM tbl WHERE amount > 100;
Który jest prawdziwym królem wydajności, nawet bez indeksu.
Z odpowiednim indeksem może być szybszy o rzędy wielkości, szczególnie w przypadku skanów tylko z indeksem.
Benchmarki
Postgres 10
Przeprowadziłem nową serię testów dla Postgres 10, w tym FILTER
klauzulę zagregowaną i wykazując rolę indeksu dla małych i dużych liczb.
Prosta konfiguracja:
CREATE TABLE tbl (
tbl_id int
, amount int NOT NULL
);
INSERT INTO tbl
SELECT g, (random() * 150)::int
FROM generate_series (1, 1000000) g;
-- only relevant for the last test
CREATE INDEX ON tbl (amount);
Rzeczywiste czasy różnią się nieco ze względu na hałas tła i specyfikę stanowiska testowego. Pokazuje typowe najlepsze czasy z większego zestawu testów. Te dwa przypadki powinny uchwycić istotę:
Test 1 zlicza ~ 1% wszystkich rzędów
SELECT COUNT(NULLIF(amount > 148, FALSE)) FROM tbl; -- 140 ms
SELECT SUM((amount > 148)::int) FROM tbl; -- 136 ms
SELECT SUM(CASE WHEN amount > 148 THEN 1 ELSE 0 END) FROM tbl; -- 133 ms
SELECT COUNT(CASE WHEN amount > 148 THEN 1 END) FROM tbl; -- 130 ms
SELECT COUNT((amount > 148) OR NULL) FROM tbl; -- 130 ms
SELECT COUNT(*) FILTER (WHERE amount > 148) FROM tbl; -- 118 ms -- !
SELECT count(*) FROM tbl WHERE amount > 148; -- without index -- 75 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 148; -- with index -- 1.4 ms -- !!!
db <> skrzypce tutaj
Test 2 zlicza ~ 33% wszystkich rzędów
SELECT COUNT(NULLIF(amount > 100, FALSE)) FROM tbl; -- 140 ms
SELECT SUM((amount > 100)::int) FROM tbl; -- 138 ms
SELECT SUM(CASE WHEN amount > 100 THEN 1 ELSE 0 END) FROM tbl; -- 139 ms
SELECT COUNT(CASE WHEN amount > 100 THEN 1 END) FROM tbl; -- 138 ms
SELECT COUNT(amount > 100 OR NULL) FROM tbl; -- 137 ms
SELECT COUNT(*) FILTER (WHERE amount > 100) FROM tbl; -- 132 ms -- !
SELECT count(*) FROM tbl WHERE amount > 100; -- without index -- 102 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 100; -- with index -- 55 ms -- !!!
db <> skrzypce tutaj
Ostatni test w każdym zestawie wykorzystał skanowanie tylko indeksu , dlatego pomógł zliczyć jedną trzecią wszystkich wierszy. Skanowanie indeksu zwykłego lub mapy bitowej nie może konkurować ze skanowaniem sekwencyjnym, jeśli obejmuje około 5% lub więcej wszystkich wierszy.
Stary test na Postgres 9.1
Aby to sprawdzić, uruchomiłem szybki test EXPLAIN ANALYZE
na prawdziwej tabeli w PostgreSQL 9.1.6.
74208 z 184568 wierszy zakwalifikowanych z warunkiem kat_id > 50
. Wszystkie zapytania zwracają ten sam wynik. Uruchomiłem każdy z nich 10 razy po kolei, aby wykluczyć efekty buforowania i dołączyłem najlepszy wynik jako notatkę:
SELECT SUM((kat_id > 50)::int) FROM log_kat; -- 438 ms
SELECT COUNT(NULLIF(kat_id > 50, FALSE)) FROM log_kat; -- 437 ms
SELECT COUNT(CASE WHEN kat_id > 50 THEN 1 END) FROM log_kat; -- 437 ms
SELECT COUNT((kat_id > 50) OR NULL) FROM log_kat; -- 436 ms
SELECT SUM(CASE WHEN kat_id > 50 THEN 1 ELSE 0 END) FROM log_kat; -- 432 ms
Prawie żadna prawdziwa różnica w wydajności.
FILTER
niż z powyższymi wyrażeniami (testowanie z pg 9.5). Czy dostajesz to samo? (WHERE
wciąż jest królem wydajności - tam, gdzie to możliwe).FILTER
Rozwiązanie jest zazwyczaj szybciej w moich testów.To jest mój test na SQL Server 2012 RTM.
Patrząc na poszczególne przebiegi i partie oddzielnie
Wyniki po uruchomieniu 5 razy (i powtórzeniu) są dość niejednoznaczne.
Pokazuje, że istnieje znacznie większa zmienność warunków pracy niż różnica między implementacją, mierzoną za pomocą ziarnistości timera programu SQL Server. Każda wersja może być na topie, a maksymalna wariancja, jaką kiedykolwiek miałem, wynosi 2,5%.
Jednak przyjmując inne podejście:
StmtText (SUM)
StmtText (COUNT)
Z mojego czytania wynika, że wersja SUM robi trochę więcej. Oprócz SUMU wykonuje COUNT . To powiedziawszy,
COUNT(*)
jest inne i powinno być szybsze niżCOUNT([Expr1004])
(pomiń NULL, więcej logiki). Rozsądny optymalizator zda sobie sprawę, że[Expr1004]
wSUM([Expr1004])
wersji SUM jest typem „int”, a zatem wykorzystuje rejestr liczb całkowitych.W każdym razie, mimo że nadal uważam, że
COUNT
wersja będzie szybsza w większości RDBMS, wyciągam wnioski z testów, że zamierzam iśćSUM(.. 1.. 0..)
w przyszłości, przynajmniej dla SQL Server bez żadnego innego powodu niż podniesienie OSTRZEŻENIA ANSI podczas używaniaCOUNT
.źródło
Z mojego doświadczenia Wykonywanie śledzenia, dla obu metod w zapytaniu około 10 000 000 zauważyłem, że Count (*) zużywa około dwa razy procesora i działa nieco szybciej. ale moje zapytania są bez filtra.
Liczyć(*)
Suma (1)
źródło