postgresql - sql - количество истинных значений


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

В приведенной выше таблице, если я это сделаю:

select count(*), count(myCol);

я получил 6, 5

Я получаю, 5поскольку он не считает нулевую запись.

Как мне также подсчитать количество истинных значений (3 в примере)?

(Это упрощение, и на самом деле я использую гораздо более сложное выражение в функции count)

Редактировать сводку: я также хочу включить в запрос простой счетчик (*), поэтому не могу использовать предложение where


«T» означает «Истина», а «f» - «Ложь»? Или вы ищете что-то вроде SELECT COUNT (DISTINCT myCol).
Шамит Верма,

Взгляните на мой второй пример, вы можете добавить WHERE myCol = trueтуда, если хотите, и если вы удалите первый, *,он просто вернет номер.
vol7ron

@Shamit да t означает истину, а f означает ложь, я обновил вопрос
EoghanM

Вы также можете не упрощать свой вопрос / запрос ... ваши требования ограничивают возможности лучшей производительности, и люди отвечают неэффективными ответами, которые получают без уважительной причины.
vol7ron

1
@ vol7ron в свою защиту должно быть некоторое упрощение, чтобы задать понятный вопрос, но да, я слишком упростил, когда изначально писал.
EoghanM

Ответы:


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

или, как вы сами убедились:

SELECT count(CASE WHEN myCol THEN 1 END) FROM <table name>

Это хороший прием, и я получил правильный ответ. Я приму это, если кто-то не предложит более короткое решение?
EoghanM

2
кроме того, по какой-либо причине вы суммировали (.. THEN 1 ELSE 0) вместо count (.. THEN true else null)?
EoghanM

5
Нет ... просто я не был уверен, какие значения будут считать count () ... и я знал, что эта сумма помогает. Но будьте осторожны: если подумать, я считаю, что sum () только для нулевых значений вернет null, поэтому для вас это должно быть COALESCE (sum (...), 0), или, другими словами, count () лучше,
Daniel

1
@EoghanM, см. Более короткий ответ с участием актеров.
Дуэйн Тоуэлл

1
Вы можете пропустить, ELSE nullчтобы получить тот же результат.
200_success

91

Преобразуйте логическое значение в целое число и сумму.

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

Вы получите 6,3.


3
Plus1: Отличный хак! Возможно, это даже быстрее, чем мое решение.
Daniel

1
Это лучшее и кратчайшее решение (имеющее аналоги во многих других средах программирования и программном обеспечении). Должно быть больше голосов

3
Очевидно, что приведение к int и count является наиболее кратким, но это не делает его лучшим. Я бы не одобрял это, потому что, хотя многие среды используют представление 0/1 для false / true, многие используют 0 / ненулевое значение, включая -1. Я согласен, что это «хакерство», и приведения достаточно рискованны, когда они не «хаки». Не буду голосовать против, но, опять же, не поддержу.
Эндрю Вулф

82

Начиная с PostgreSQL 9.4, есть FILTERпредложение , позволяющее очень кратким запросом подсчитывать истинные значения:

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

Вышеупомянутый запрос является плохим примером, поскольку достаточно простого предложения WHERE и предназначен только для демонстрации синтаксиса. Предложение FILTER выгодно отличается тем, что его легко комбинировать с другими агрегатами:

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

Это предложение особенно удобно для агрегатов в столбце, который использует другой столбец в качестве предиката, позволяя при этом получать агрегаты с различными фильтрами в одном запросе:

select count(*),
       sum(otherCol) filter (where myCol)
from tbl;

2
Это лучший ответ для PG> 9.4, и он невероятно быстр
Хуан Рикардо

47

вероятно, лучший подход - использовать функцию nullif.

В основном

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

или короче

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


2
Ваше «в целом» выглядит неправильно: AFAICS, nullif([boolean expression], true)вернет, falseесли [логическое выражение] ложно, а nullесли оно истинно, то вы будете считать ложные значения. Я думаю ты хочешь nullif([boolean expression], false).
rjmunro

да, в «общем» случае должно быть наоборот. фиксированный. Спасибо.
wrobell

1
Юк. Это исправление действительно сбивает с толку. AFAICS, теперь он будет считать истинные или нулевые значения. Я думаю, что если перефразировать его так, чтобы он всегда был у вас, то nullif([boolean expression], false)его будет намного легче читать. Затем вы можете изменить часть логического выражения так, как вам нравится, в этом случае myCol = trueдля подсчета истинных значений, или myCol = falseдля подсчета ложных значений, или name='john'для подсчета людей по имени Джон и т. Д.
rjmunro

19

Самым коротким и ленивым (без кастинга) решением было бы использовать формулу:

SELECT COUNT(myCol OR NULL) FROM myTable;

Попробуй сам:

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

дает тот же результат, что и

SELECT SUM(CASE WHEN x < 7 THEN 1 ELSE 0 END)
   FROM GENERATE_SERIES(0,10) t(x);

Это определенно более приятное решение, чем мое :)
Daniel

Очень содержательный ответ.
lucasarruda

7

В MySQL вы также можете сделать это:

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

Я думаю, что в Postgres это работает:

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

или лучше (чтобы избежать :: и использовать стандартный синтаксис SQL):

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

Это самое простое решение, которое я когда-либо видел ^ _ ^
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

Или может это

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;

+1 Если myColвыражение является логическим, вы можете заменить проверку наwhere (myCol)
ypercubeᵀᴹ

извините, я слишком упростил свой пример: я не могу использовать предложение where, так как я также хочу вернуть общий счетчик, представляющий общее количество строк, а также количество истинных значений.
EoghanM

7

Просто преобразуйте логическое поле в целое число и просуммируйте. Это будет работать на postgresql:

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

Надеюсь, это поможет!


Это не быстрее и не точнее других решений. Я считаю, что вы пришли из Oracle, когда используете int, поскольку логическое значение более интуитивно для вас.
Даниэль

4
SELECT count(*)         -- or count(myCol)
FROM   <table name>     -- replace <table name> with your table
WHERE  myCol = true;

Вот способ использования оконной функции:

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

-- Outputs:
-- --------------
-- myCol | count
-- ------+-------
--  f    |  2
--  t    |  3
--       |  1

извините, я не могу вернуть несколько строк для более сложного примера, к которому я применяю это решение.
EoghanM

Да, но вы можете еще больше ограничить его, просто добавив WHERE myCol = true. Я привел второй пример не потому, что он быстрее, а скорее как обучающий элемент для оконных функций Postgres, с которыми многие пользователи не знакомы или о которых не знают.
vol7ron

0
select count(myCol)
from mytable
group by myCol
;

сгруппирует 3 возможных состояния bool (false, true, 0) в три строки, что особенно удобно при группировке вместе с другим столбцом, таким как день

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.