postgresql - sql - contagem de valores `true`

97
myCol
------
 true
 true
 true
 false
 false
 null

Na tabela acima, se eu fizer:

select count(*), count(myCol);

eu recebo 6, 5

Eu entendo 5, pois não conta a entrada nula.

Como também conto o número de valores verdadeiros (3 no exemplo)?

(Esta é uma simplificação e, na verdade, estou usando uma expressão muito mais complicada na função de contagem)

Editar resumo: também quero incluir uma contagem simples (*) na consulta, portanto, não posso usar uma cláusula where

EoghanM
fonte
'T' representa True e 'f' significa False? Ou você está procurando por algo como SELECT COUNT (DISTINCT myCol).
Shamit Verma
dê uma olhada no meu segundo exemplo, você pode adicionar um WHERE myCol = truelá se quiser e se remover o primeiro, *,ele apenas retornará o número.
vol7ron,
@Shamit sim t significa verdadeiro ef significa falso, atualizei a pergunta
EoghanM
Você também pode não simplificar sua pergunta / consulta ... seus requisitos restringem as possibilidades de melhor desempenho e as pessoas estão respondendo com respostas ineficientes, que estão sendo aumentadas sem um bom motivo.
vol7ron
1
@ vol7ron em minha defesa tem que haver alguma simplificação para fazer uma pergunta compreensível, mas sim, simplifiquei demais quando postei originalmente.
EoghanM

Respostas:

132
SELECT COALESCE(sum(CASE WHEN myCol THEN 1 ELSE 0 END),0) FROM <table name>

ou, como você descobriu por si mesmo:

SELECT count(CASE WHEN myCol THEN 1 END) FROM <table name>
Daniel
fonte
Este é um bom hack e obteve a resposta certa de mim. Vou aceitar, a menos que alguém apareça com uma solução mais curta.
EoghanM
2
Além disso, alguma razão pela qual você fez soma (.. THEN 1 ELSE 0) em vez de contar (.. THEN true else null)?
EoghanM
5
Não ... é só que eu não tinha certeza de quais valores contariam () contariam ... e eu sabia que aquela soma funcionava. Mas cuidado: pensando bem, acredito que sum () sobre apenas valores nulos retornará nulo, então deve ser COALESCE (sum (...), 0) para você, ou, em outras palavras, count () é melhor,
Daniel,
1
@EoghanM, veja uma resposta mais curta envolvendo elenco.
Dwayne Towell
1
Você pode omitir ELSE nullpara obter o mesmo resultado.
200_success
91

Converta o Booleano em um número inteiro e some.

SELECT count(*),sum(myCol::int);

Você consegue 6,3.

Dwayne Towell
fonte
3
Plus1: Belo hack! Isso provavelmente é ainda mais rápido do que a minha solução.
Daniel
1
Esta é a melhor e mais curta solução (e tem equivalências em muitos outros ambientes de programação e software). Deveria ser mais votado
3
O 'cast to int and count' é claramente o mais conciso, mas isso não o torna o melhor. Eu não endossaria isso, porque enquanto muitos ambientes usam a representação 0/1 para falso / verdadeiro, muitos usam 0 / diferente de zero, incluindo -1. Eu concordo que é um "hack" e os lançamentos são arriscados o suficiente quando não são "hacks". Não vai votar negativamente, mas, novamente, não vai endossar.
Andrew Wolfe
79

Desde o PostgreSQL 9.4 existe a FILTERcláusula , que permite uma consulta muito concisa para contar os valores verdadeiros:

select count(*) filter (where myCol)
from tbl;

A consulta acima é um mau exemplo, pois uma cláusula WHERE simples seria suficiente e serve apenas para demonstrar a sintaxe. O ponto forte da cláusula FILTER é que é fácil de combinar com outros agregados:

select count(*), -- all
       count(myCol), -- non null
       count(*) filter (where myCol) -- true
from tbl;

A cláusula é especialmente útil para agregados em uma coluna que usa outra coluna como predicado, enquanto permite buscar agregados filtrados de forma diferente em uma única consulta:

select count(*),
       sum(otherCol) filter (where myCol)
from tbl;
Ilja Everilä
fonte
2
Esta é a melhor resposta para PG> 9.4 e é incrivelmente rápida
Juan Ricardo
47

provavelmente, a melhor abordagem é usar a função nullif.

em geral

select
    count(nullif(myCol = false, true)),  -- count true values
    count(nullif(myCol = true, true)),   -- count false values
    count(myCol);

ou em suma

select
    count(nullif(myCol, true)),  -- count false values
    count(nullif(myCol, false)), -- count true values
    count(myCol);

http://www.postgresql.org/docs/9.0/static/functions-conditional.html

Wrobell
fonte
2
Seu "em geral" parece errado: AFAICS, nullif([boolean expression], true)retornará falsese [expressão booleana] for falsa, e nullse for verdadeira, então você estará contando os valores falsos. Eu acho que você quer nullif([boolean expression], false).
rjmunro
sim, o caso "geral" deveria ser o contrário. fixo. obrigado.
Wrobell
1
Yuk. Essa correção é realmente confusa. AFAICS, agora contará valores verdadeiros ou nulos. Acho que reformulá-lo para que você sempre tenha tornado nullif([boolean expression], false)muito mais fácil de ler. Você pode então variar a parte da expressão booleana para ser o que quiser, neste caso myCol = truepara contar valores verdadeiros, ou myCol = falsepara contar valores falsos, ou name='john'para contar pessoas chamadas john etc.
rjmunro
19

A solução mais curta e preguiçosa (sem fundir) seria usar a fórmula:

SELECT COUNT(myCol OR NULL) FROM myTable;

Tente você mesmo:

SELECT COUNT(x < 7 OR NULL)
   FROM GENERATE_SERIES(0,10) t(x);

dá o mesmo resultado que

SELECT SUM(CASE WHEN x < 7 THEN 1 ELSE 0 END)
   FROM GENERATE_SERIES(0,10) t(x);
Le Droid
fonte
Esta é definitivamente uma solução melhor do que a minha :)
Daniel
Resposta muito perspicaz.
lucasarruda
7

No MySQL, você também pode fazer isso:

SELECT count(*) AS total
     , sum(myCol) AS countTrue --yes, you can add TRUEs as TRUE=1 and FALSE=0 !!
FROM yourTable
;

Acho que no Postgres isso funciona:

SELECT count(*) AS total
     , sum(myCol::int) AS countTrue --convert Boolean to Integer
FROM yourTable
;

ou melhor (para evitar :: e usar a sintaxe SQL padrão):

SELECT count(*) AS total
     , sum(CAST(myCol AS int)) AS countTrue --convert Boolean to Integer
FROM yourTable
;
ypercubeᵀᴹ
fonte
Esta é a solução mais simples que já vi ^ _ ^
JiaHao Xu
7
select f1,
       CASE WHEN f1 = 't' THEN COUNT(*) 
            WHEN f1 = 'f' THEN COUNT(*) 
            END AS counts,
       (SELECT COUNT(*) FROM mytable) AS total_counts
from mytable
group by f1

Ou talvez isso

SELECT SUM(CASE WHEN f1 = 't' THEN 1 END) AS t,
       SUM(CASE WHEN f1 = 'f' THEN 1 END) AS f,
       SUM(CASE WHEN f1 NOT IN ('t','f') OR f1 IS NULL THEN 1 END) AS others,
       SUM(CASE WHEN f1 IS NOT NULL OR f1 IS NULL THEN 1 ELSE 0 END) AS total_count
FROM mytable;
Kuberchaun
fonte
+1 Se a myColexpressão for um booleano, você pode substituir a verificação porwhere (myCol)
ypercubeᵀᴹ
desculpe, simplifiquei demais meu exemplo: não posso usar uma cláusula where porque também quero retornar uma contagem total que representa o número total de linhas, bem como uma contagem dos valores verdadeiros.
EoghanM
7

Basta converter o campo booleano em inteiro e fazer uma soma. Isso funcionará no postgresql:

select sum(myCol::int) from <table name>

Espero que ajude!

Jaspreet Singh
fonte
Não é mais rápido nem mais preciso do que as outras soluções. Eu acredito que você vem do Oracle quando usar ints como booleano é mais intuitivo para você.
Daniel
4
SELECT count(*)         -- or count(myCol)
FROM   <table name>     -- replace <table name> with your table
WHERE  myCol = true;

Esta é uma maneira de usar a função de janela:

SELECT DISTINCT *, count(*) over(partition by myCol)
FROM   <table name>;

-- Outputs:
-- --------------
-- myCol | count
-- ------+-------
--  f    |  2
--  t    |  3
--       |  1
Vol7ron
fonte
desculpe, não posso retornar várias linhas para o exemplo mais complicado ao qual estou aplicando esta solução.
EoghanM
Sim, mas você pode restringir ainda mais apenas adicionando WHERE myCol = true. Forneci o segundo exemplo não porque seja mais rápido, mas mais como uma peça educacional para as funções de janelas do Postgres, com as quais muitos usuários não se sentem confortáveis ​​ou não sabem.
vol7ron
0
select count(myCol)
from mytable
group by myCol
;

irá agrupar os 3 estados possíveis de bool (falso, verdadeiro, 0) em três linhas especialmente úteis ao agrupar junto com outra coluna como dia

perigo 5000
fonte