Você já respondeu a sua pergunta principalmente. Eu tenho alguns pedaços para adicionar:
No PostgreSQL (e outros RDBMS compatíveis com o boolean
tipo), você pode usar o boolean
resultado do teste diretamente. Transmitir para integer
e SUM()
:
SUM((amount > 100)::int))
Ou use-o em uma NULLIF()
expressão e COUNT()
:
COUNT(NULLIF(amount > 100, FALSE))
Ou com um simples OR NULL
:
COUNT(amount > 100 OR NULL)
Ou várias outras expressões. O desempenho é quase idêntico . COUNT()
normalmente é um pouco mais rápido que SUM()
. Ao contrário SUM()
e como Paulo já comentou , COUNT()
nunca retorna NULL
, o que pode ser conveniente. Relacionado:
Desde o Postgres 9.4, há também a FILTER
cláusula . Detalhes:
É mais rápido que todos os itens acima em cerca de 5 a 10%:
COUNT(*) FILTER (WHERE amount > 100)
Se a consulta for tão simples quanto o seu caso de teste, com apenas uma contagem e nada mais, você poderá reescrever:
SELECT count(*) FROM tbl WHERE amount > 100;
Qual é o verdadeiro rei do desempenho, mesmo sem índice.
Com um índice aplicável, ele pode ser mais rápido em ordens de magnitude, especialmente com varreduras somente de índice.
Benchmarks
Postgres 10
Fiz uma nova série de testes para o Postgres 10, incluindo a FILTER
cláusula agregada e demonstrando o papel de um índice para pequenas e grandes contagens.
Configuração simples:
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);
Os tempos reais variam bastante devido ao ruído de fundo e às especificidades do banco de ensaio. Mostrando os melhores horários típicos de um conjunto maior de testes. Esses dois casos devem capturar a essência:
Teste 1 contando ~ 1% de todas as linhas
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 <> mexer aqui
Teste 2 contando ~ 33% de todas as linhas
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 <> mexer aqui
O último teste em cada conjunto usou uma varredura apenas de índice , e é por isso que ajudou a contar um terço de todas as linhas. As varreduras de índice simples ou de índice de bitmap não podem competir com uma varredura seqüencial ao envolver aproximadamente 5% ou mais de todas as linhas.
Teste antigo para o Postgres 9.1
Para verificar, executei um teste rápido EXPLAIN ANALYZE
em uma tabela da vida real no PostgreSQL 9.1.6.
74208 de 184568 linhas qualificadas com a condição kat_id > 50
. Todas as consultas retornam o mesmo resultado. Fiz cada uma delas 10 vezes seguidas para excluir efeitos de cache e anexei o melhor 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
Quase nenhuma diferença real no desempenho.
FILTER
que com as expressões acima (teste na página 9.5). Você consegue o mesmo? (WHERE
ainda é rei do desempenho - sempre que possível).FILTER
solução é geralmente mais rápida nos meus testes.Este é o meu teste no SQL Server 2012 RTM.
Analisando execuções individuais e lotes separadamente
Os resultados depois de executar 5 vezes (e repetir) são bastante inconclusivos.
Isso mostra que há muito mais variabilidade nas condições de execução do que a diferença entre a implementação, quando medida com a granularidade do timer do SQL Server. Qualquer uma das versões pode vir ao topo, e a variação máxima que eu já recebi é de 2,5%.
No entanto, adotando uma abordagem diferente:
StmtText (SUM)
StmtText (COUNT)
Pela minha leitura, parece que a versão SUM faz um pouco mais. Ele está executando um COUNT além de um SUM. Dito isto,
COUNT(*)
é diferente e deve ser mais rápido queCOUNT([Expr1004])
(pule NULLs, mais lógica). Um otimizador razoável perceberá que[Expr1004]
naSUM([Expr1004])
versão SUM é um tipo "int" e, portanto, utiliza um registro inteiro.De qualquer forma, embora eu ainda acredite que a
COUNT
versão será mais rápida na maioria dos RDBMS, minha conclusão dos testes é que eu continuareiSUM(.. 1.. 0..)
no futuro, pelo menos para o SQL Server por nenhum outro motivo que os ANSI WARNINGS sendo disparados ao usarCOUNT
.fonte
Na minha experiência Fazendo um rastreamento, para ambos os métodos em uma Consulta de cerca de 10.000.000, observei que o Count (*) usa cerca de duas vezes da CPU e roda um pouco mais rápido. mas minhas consultas estão sem filtro.
Contagem(*)
Soma (1)
fonte