postgresql - sql - `true` 值的计数

Posted

技术标签:

【中文标题】postgresql - sql - `true` 值的计数【英文标题】:postgresql - sql - count of `true` values 【发布时间】:2011-03-22 19:07:51 【问题描述】:
myCol
------
 true
 true
 true
 false
 false
 null

在上表中,如果我这样做:

select count(*), count(myCol);

我收到6, 5

我得到5,因为它不计算空条目。

我如何也计算真值的数量(示例中为 3)?

(这是一种简化,实际上我在 count 函数中使用了更复杂的表达式)

编辑摘要:我还想在查询中包含一个普通的 count(*),所以不能使用 where 子句

【问题讨论】:

't' 代表 True 和 'f' 代表 False 吗?或者您是否在寻找类似 SELECT COUNT(DISTINCT myCol) 的东西。 看看我的第二个例子,如果你愿意,你可以在里面扔一个WHERE myCol = true,如果你删除第一个*,,它只会返回数字。 @Shamit ye​​s t 代表 true,f 代表 false,我已经更新了问题 您最好不要简化您的问题/查询...您的要求限制了更好的性能可能性,人们以低效的答案做出回应,这些答案无缘无故地被提高了。 @vol7ron 在我的辩护中,为了提出一个易于理解的问题,必须进行一些简化,但是是的,我最初发布时过于简化了。 【参考方案1】:
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>

【讨论】:

另外,你为什么用 sum(.. THEN 1 ELSE 0) 而不是 count(.. THEN true else null) ? 不...只是我不确定哪些值会 count() 计数...而且我知道 sum 可以解决问题。但要注意:再想一想,我相信 sum() 仅对 null 值会返回 null,所以它应该是 COALESCE(sum(...),0) ,或者换句话说, count() 更好, 谢谢!从未想过我的查询会在一百万年后起作用! @EoghanM,请参阅涉及演员阵容的简短答案。 您实际上可以省略ELSE null 以获得相同的结果。【参考方案2】:

从 PostgreSQL 9.4 开始有了FILTER clause,它允许非常简洁的查询来计算真实值:

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;

【讨论】:

【参考方案3】:

将布尔值转换为整数并求和。

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

你会得到6,3

【讨论】:

Plus1:不错的技巧!这可能比我的解决方案还要快。 这是最好和最短的解决方案(并且在许多其他编程环境和软件中具有等效性)。应该更多地投票 'cast to int and count' 显然是最简洁的,但这并不是最好的。我不会赞同这一点,因为虽然许多环境使用 0/1 表示假/真,但许多环境使用 0/非零,包括 -1。我同意这是一个“黑客”,当他们不是“黑客”时,演员阵容就足够冒险了。不会投反对票,但不会再次支持。【参考方案4】:

可能,最好的方法是使用 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

【讨论】:

你的“一般”看起来是错误的:AFAICS,nullif([boolean expression], true) 将返回 false 如果 [boolean expression] 为假,null 如果它是真的,所以你会算作假价值观。我想你想要nullif([boolean expression], false) 是的,“一般”情况应该是相反的。固定的。谢谢。 哎呀。这个修复真的很混乱。 AFAICS,它现在将计算真值或空值。我认为改写它以便您始终拥有nullif([boolean expression], false) 使其更易于阅读。然后,您可以将布尔表达式部分更改为您喜欢的任何内容,在这种情况下,myCol = true 用于计算真值,myCol = false 用于计算错误值,或name='john' 用于计算称为 john 的人等。【参考方案5】:

最短和最懒(不强制转换)的解决方案是使用以下公式:

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);

【讨论】:

这绝对是比我更好的解决方案 :) 非常有见地的答案。【参考方案6】:
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) 抱歉,我的示例过于简单了:我不能使用 where 子句,因为我还想返回表示总行数的总计数以及真实值的计数。跨度> 【参考方案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
;

【讨论】:

这是我见过的最简单的解决方案^_^【参考方案8】:

只需将布尔字段转换为整数并求和。这将适用于 postgresql:

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

希望有帮助!

【讨论】:

它并不比其他解决方案更快也更精确。我相信您来自 Oracle,因为使用 int 作为布尔值对您来说更直观。【参考方案9】:
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

【讨论】:

抱歉,我无法为应用此解决方案的更复杂示例返回多行。 是的,但您可以通过添加 WHERE myCol = true 来进一步限制它。我提供第二个示例不是因为它更快,而是更多地作为 Postgres 窗口函数的教育部分,许多用户对此感到不满意或不知道。【参考方案10】:

基准测试

TL;DR:采用您喜欢的解决方案。没有显着差异。

实用脚本

before()
    psql <<-SQL
        create table bench (
                id         serial
            , thebool    boolean
        );

        insert into bench (thebool)
        select (random() > 0.5)
        from generate_series(1, 1e6) g;


        analyze bench;
    SQL

after()
    psql -c 'drop table bench'

test()
    echo $(tput bold)$1$(tput sgr0)
    psql -c "explain analyze select $1 from bench" | tail -4 | head -2

实际基准

在 1.4GHz i5 MacBookPro、psql 和 pg 12.4(Linux docker 容器中的 pg)上制作:

before  
test 'count(*) filter (where thebool)'
# Planning Time: 0.138 ms
# Execution Time: 4424.042 ms
test 'count(case when thebool then 1 end)'
# Planning Time: 0.156 ms
# Execution Time: 4638.861 ms
test 'count(nullif(thebool, false))'
# Planning Time: 0.201 ms
# Execution Time: 5267.631 ms
test 'count(thebool or null)'
# Planning Time: 0.202 ms
# Execution Time: 4672.700 ms
test 'sum(thebool::integer)'
# Planning Time: 0.155 ms
# Execution Time: 4602.406 ms
test 'coalesce(sum(case when thebool THEN 1 ELSE 0 END), 0)'
# Planning Time: 0.167 ms
# Execution Time: 4416.503 ms
after

【讨论】:

【参考方案11】:
select count(myCol)
from mytable
group by myCol
;

将 bool 的 3 种可能状态 (false, true, 0) 分组为三行 与另一列(如 day)组合在一起时特别方便

【讨论】:

以上是关于postgresql - sql - `true` 值的计数的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL之SQL操作符介绍及实践

PostgreSQL谓词之EXISTS

PostgreSQL数据类型-boolean

在 postgresql 查询中检查布尔真/假结果

PostgreSQL全文检索简介

PostgreSQL字段类型说明