Sie haben die Frage meistens schon selbst beantwortet. Ich habe ein paar Kleinigkeiten hinzuzufügen:
In PostgreSQL (und anderen RDBMS, die den boolean
Typ unterstützen) können Sie das boolean
Ergebnis des Tests direkt verwenden. Cast it to integer
und SUM()
:
SUM((amount > 100)::int))
Oder verwende es in einem NULLIF()
Ausdruck und COUNT()
:
COUNT(NULLIF(amount > 100, FALSE))
Oder mit einem einfachen OR NULL
:
COUNT(amount > 100 OR NULL)
Oder verschiedene andere Ausdrücke. Die Leistung ist nahezu identisch . COUNT()
ist in der Regel etwas schneller als SUM()
. Im Gegensatz zu SUM()
und wie Paulus es bereits kommentierte , COUNT()
kehrt er niemals zurück NULL
, was vielleicht praktisch ist. Verbunden:
Seit Postgres 9.4 gibt es auch die FILTER
Klausel . Einzelheiten:
Es ist um 5 - 10% schneller als alle oben genannten:
COUNT(*) FILTER (WHERE amount > 100)
Wenn die Abfrage so einfach wie Ihr Testfall ist und nur eine einzige Anzahl und nichts anderes enthält, können Sie Folgendes umschreiben:
SELECT count(*) FROM tbl WHERE amount > 100;
Welches ist der wahre König der Leistung, auch ohne Index.
Mit einem anwendbaren Index kann es um Größenordnungen schneller sein, insbesondere bei Nur-Index-Scans.
Benchmarks
Postgres 10
Ich habe eine neue Testreihe für Postgres 10 durchgeführt, einschließlich der Aggregatklausel FILTER
und der Darstellung der Rolle eines Index für kleine und große Zahlen.
Einfaches Setup:
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);
Die tatsächlichen Zeiten variieren aufgrund von Hintergrundgeräuschen und Besonderheiten des Prüfstands erheblich. Zeigen Sie typische Bestzeiten aus einer größeren Reihe von Tests. Diese beiden Fälle sollten das Wesentliche erfassen:
Test 1 mit ~ 1% aller Zeilen
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 <> hier fummeln
Test 2 mit ~ 33% aller Zeilen
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 <> hier fummeln
Der letzte Test in jedem Satz verwendete einen Nur-Index- Scan, weshalb er dazu beitrug, ein Drittel aller Zeilen zu zählen. Einfache Index- oder Bitmap-Index-Scans können nicht mit einem sequentiellen Scan konkurrieren, wenn ungefähr 5% oder mehr aller Zeilen betroffen sind.
Alter Test für Postgres 9.1
Zur Verifizierung habe ich EXPLAIN ANALYZE
in PostgreSQL 9.1.6 einen Schnelltest mit einer echten Tabelle durchgeführt.
74208 von 184568 Zeilen qualifizierten sich mit der Bedingung kat_id > 50
. Alle Abfragen geben das gleiche Ergebnis zurück. Ich habe jeweils 10-mal nacheinander ausgeführt, um Caching-Effekte auszuschließen, und das beste Ergebnis als Anmerkung angehängt:
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
Kaum ein wirklicher Leistungsunterschied.
FILTER
als mit den obigen Ausdrücken (Testen mit S. 9.5). Bekommst du das gleiche? (WHERE
ist immer noch König der Leistung - wo möglich).FILTER
Lösung ist in meinen Tests normalerweise schneller.Dies ist mein Test unter SQL Server 2012 RTM.
Einzelne Läufe und Chargen getrennt betrachten
Die Ergebnisse nach 5-maligem Ausführen (und Wiederholen) sind ziemlich nicht schlüssig.
Es zeigt, dass die Ausführungsbedingungen weitaus variabler sind als die Implementierung, gemessen an der Granularität des SQL Server-Timers. Beide Versionen können die Oberhand gewinnen, und die maximale Varianz, die ich je hatte, beträgt 2,5%.
Ein anderer Ansatz:
StmtText (SUM)
StmtText (COUNT)
Meiner Lektüre nach scheint die SUM-Version etwas mehr zu leisten. Sie führt zusätzlich zu einer SUMME eine COUNT durch . Having said that, ist
COUNT(*)
anders und sollte schneller sein alsCOUNT([Expr1004])
(überspringen NULLs, mehr Logik). Ein vernünftiger Optimierer wird erkennen, dass[Expr1004]
inSUM([Expr1004])
der SUM-Version ein "int" -Typ vorliegt und daher ein Ganzzahlregister verwendet wird.Auf jeden Fall, obwohl ich immer noch glaube, dass die
COUNT
Version in den meisten RDBMS schneller sein wird, ist meine Schlussfolgerung aus den Tests, dass ichSUM(.. 1.. 0..)
in Zukunft damit umgehen werde, zumindest für SQL Server aus keinem anderen Grund als den ANSI-WARNUNGEN, die bei der Verwendung ausgegeben werdenCOUNT
.quelle
Nach meiner Erfahrung habe ich festgestellt, dass bei beiden Methoden in einer Abfrage von etwa 10.000.000 die Anzahl (*) etwa das Zweifache der CPU ausmacht und etwas schneller ausgeführt wird. aber meine Abfragen sind ohne Filter.
Anzahl(*)
Summe (1)
quelle