Sobre todo ya respondiste la pregunta tú mismo. Tengo algunos bocados para agregar:
En PostgreSQL (y otros RDBMS que admiten el boolean
tipo) puede usar el boolean
resultado de la prueba directamente. Echarlo a integer
y SUM()
:
SUM((amount > 100)::int))
O úsalo en una NULLIF()
expresión y COUNT()
:
COUNT(NULLIF(amount > 100, FALSE))
O con un simple OR NULL
:
COUNT(amount > 100 OR NULL)
O varias otras expresiones. El rendimiento es casi idéntico . COUNT()
es típicamente muy ligeramente más rápido que SUM()
. A diferencia SUM()
y como ya comentó Paul , COUNT()
nunca regresa NULL
, lo que puede ser conveniente. Relacionado:
Desde Postgres 9.4 también existe la FILTER
cláusula . Detalles:
Es más rápido que todo lo anterior en alrededor del 5 al 10%:
COUNT(*) FILTER (WHERE amount > 100)
Si la consulta es tan simple como su caso de prueba, con un solo recuento y nada más, puede volver a escribir:
SELECT count(*) FROM tbl WHERE amount > 100;
Cuál es el verdadero rey del rendimiento, incluso sin índice.
Con un índice aplicable, puede ser más rápido en órdenes de magnitud, especialmente con escaneos de solo índice.
Puntos de referencia
Postgres 10
Ejecuté una nueva serie de pruebas para Postgres 10, incluida la FILTER
cláusula agregada y demostrando el papel de un índice para recuentos pequeños y grandes.
Configuración 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);
Los tiempos reales varían bastante debido al ruido de fondo y los detalles del banco de pruebas. Mostrando los mejores tiempos típicos de un conjunto mayor de pruebas. Estos dos casos deberían capturar la esencia:
Prueba 1 contando ~ 1% de todas las filas
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 <> violín aquí
Prueba 2 contando ~ 33% de todas las filas
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 <> violín aquí
La última prueba en cada conjunto utilizó un escaneo de solo índice , por lo que ayudó a contar un tercio de todas las filas. Los escaneos de índice simple o de mapa de bits no pueden competir con un escaneo secuencial cuando involucran aproximadamente el 5% o más de todas las filas.
Antigua prueba para Postgres 9.1
Para verificar, ejecuté una prueba rápida con EXPLAIN ANALYZE
una tabla de la vida real en PostgreSQL 9.1.6.
74208 de 184568 filas calificadas con la condición kat_id > 50
. Todas las consultas devuelven el mismo resultado. Ejecuté cada uno 10 veces por turno para excluir los efectos de almacenamiento en caché y agregué el mejor resultado como nota:
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
Casi ninguna diferencia real en el rendimiento.
FILTER
que con las expresiones anteriores (prueba con la página 9.5). ¿Obtienes lo mismo? (WHERE
sigue siendo el rey del rendimiento, siempre que sea posible).FILTER
solución suele ser más rápida en mis pruebas.Esta es mi prueba en SQL Server 2012 RTM.
Mirando carreras individuales y lotes por separado
Los resultados después de correr 5 veces (y repetir) no son concluyentes.
Muestra que hay mucha más variabilidad en las condiciones de ejecución que diferencias entre la implementación, cuando se mide con la granularidad del temporizador de SQL Server. Cualquiera de las versiones puede llegar a la cima, y la variación máxima que he obtenido es del 2.5%.
Sin embargo, adoptando un enfoque diferente:
StmtText (SUMA)
StmtText (COUNT)
Según mi lectura, parece que la versión SUM hace un poco más. Está realizando un RECUENTO además de una SUMA. Dicho esto,
COUNT(*)
es diferente y debería ser más rápido queCOUNT([Expr1004])
(omitir NULL, más lógica). Un optimizador razonable se dará cuenta de que[Expr1004]
enSUM([Expr1004])
la versión SUM es un tipo "int" y, por lo tanto, utiliza un registro entero.En cualquier caso, aunque sigo creyendo que la
COUNT
versión será más rápida en la mayoría de los RDBMS, mi conclusión de las pruebas es que iréSUM(.. 1.. 0..)
en el futuro, al menos para SQL Server por ninguna otra razón que las ADVERTENCIAS ANSI que se generan al usarCOUNT
.fuente
En mi experiencia, haciendo un seguimiento, para ambos métodos en una consulta de aproximadamente 10,000,000 me di cuenta de que Count (*) usa alrededor de dos veces de CPU y corre un poco más rápido. pero mis consultas no tienen filtro.
Contar(*)
Suma (1)
fuente