Vous avez déjà répondu à la question vous-même. J'ai quelques morceaux à ajouter:
Dans PostgreSQL (et les autres SGBDR qui prennent en charge le boolean
type), vous pouvez utiliser boolean
directement le résultat du test. Castez-le integer
et SUM()
:
SUM((amount > 100)::int))
Ou utilisez-le dans une NULLIF()
expression et COUNT()
:
COUNT(NULLIF(amount > 100, FALSE))
Ou avec un simple OR NULL
:
COUNT(amount > 100 OR NULL)
Ou diverses autres expressions. Les performances sont presque identiques . COUNT()
est généralement très légèrement plus rapide que SUM()
. Contrairement SUM()
et comme Paul l'a déjà commenté , COUNT()
ne revient jamais NULL
, ce qui peut être pratique. En relation:
Depuis Postgres 9.4, il y a aussi la FILTER
clause . Détails:
C'est plus rapide que tout ce qui précède d'environ 5 à 10%:
COUNT(*) FILTER (WHERE amount > 100)
Si la requête est aussi simple que votre cas de test, avec un seul décompte et rien d'autre, vous pouvez réécrire:
SELECT count(*) FROM tbl WHERE amount > 100;
Qui est le vrai roi de la performance, même sans indice.
Avec un index applicable, il peut être plus rapide de plusieurs ordres de grandeur, en particulier avec les analyses d'index uniquement.
Repères
Postgres 10
J'ai exécuté une nouvelle série de tests pour Postgres 10, y compris la FILTER
clause d' agrégation et démontrant le rôle d'un indice pour les petits et les grands comptes.
Configuration simple:
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);
Les temps réels varient beaucoup en raison du bruit de fond et des spécificités du banc d'essai. Affichage des meilleurs temps typiques à partir d'un plus grand ensemble de tests. Ces deux cas devraient saisir l'essence:
Test 1 comptant ~ 1% de toutes les lignes
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 <> violon ici
Test 2 comptant ~ 33% de toutes les lignes
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 <> violon ici
Le dernier test de chaque ensemble a utilisé un index uniquement analyse d' , c'est pourquoi il a aidé à compter un tiers de toutes les lignes. Les analyses d'index simple ou d'index bitmap ne peuvent pas rivaliser avec une analyse séquentielle lorsqu'elles impliquent environ 5% ou plus de toutes les lignes.
Ancien test pour Postgres 9.1
Pour vérifier, j'ai effectué un test rapide avec EXPLAIN ANALYZE
une table réelle dans PostgreSQL 9.1.6.
74208 des 184568 lignes qualifiées avec la condition kat_id > 50
. Toutes les requêtes renvoient le même résultat. J'ai couru chacun 10 fois à tour de rôle pour exclure les effets de mise en cache et j'ai ajouté le meilleur résultat comme note:
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
Presque aucune réelle différence de performances.
FILTER
qu'avec les expressions ci-dessus (test avec pg 9.5). Obtenez-vous la même chose? (WHERE
est toujours roi de la performance - si possible).FILTER
solution est généralement plus rapide dans mes tests.Ceci est mon test sur SQL Server 2012 RTM.
Examen des analyses et des lots individuels séparément
Les résultats après avoir exécuté 5 fois (et répété) ne sont pas concluants.
Cela montre qu'il y a beaucoup plus de variabilité dans les conditions d'exécution qu'il n'y a de différence entre l'implémentation, mesurée avec la granularité du temporisateur SQL Server. L'une ou l'autre version peut venir en haut, et l'écart maximum que j'ai jamais obtenu est de 2,5%.
Cependant, en adoptant une approche différente:
StmtText (SUM)
StmtText (COUNT)
D'après ma lecture, il semblerait que la version SUM fasse un peu plus. Il exécute un COUNT en plus d' un SUM. Cela dit,
COUNT(*)
c'est différent et devrait être plus rapide queCOUNT([Expr1004])
(sauter les NULL, plus de logique). Un optimiseur raisonnable se rendra compte que[Expr1004]
dansSUM([Expr1004])
la version SUM est un type "int" et utilisera donc un registre entier.Dans tous les cas, même si je crois toujours que la
COUNT
version sera plus rapide dans la plupart des SGBDR, ma conclusion des tests est que je vais continuer àSUM(.. 1.. 0..)
l'avenir, au moins pour SQL Server pour aucune autre raison que les avertissements ANSI générés lors de l'utilisationCOUNT
.la source
Dans Mon expérience Faire une trace, pour les deux méthodes dans une requête d'environ 10 000 000, j'ai remarqué que Count (*) utilise environ deux fois le processeur et s'exécute un peu plus rapidement. mais mes requêtes sont sans filtre.
Compter(*)
Somme (1)
la source