最稳妥方式是用count(case when...)统计布尔值:count(case when status then 1 end)统计true,count(case when not status then 1 end)统计false,count(*) - count(status)统计null;bool_and/bool_or仅返回布尔结果,不可用于计数。

用 COUNT + CASE 统计真假数量最稳妥
PostgreSQL 没有内置的 COUNT_TRUE 或 COUNT_FALSE 函数,直接对布尔列用 COUNT(is_active) 只会统计非 NULL 值的行数,无法区分 TRUE/FALSE。必须用 CASE 显式分类再聚合。
-
COUNT(CASE WHEN status THEN 1 END)统计 TRUE 行数(自动跳过 FALSE 和 NULL) -
COUNT(CASE WHEN NOT status THEN 1 END)统计 FALSE 行数(注意:NOT 对 NULL 返回 NULL,所以也跳过) -
COUNT(*) - COUNT(status)算出 NULL 行数(因为COUNT(status)只计非 NULL)
示例:统计每类订单中已发货/未发货/待确认的数量
SELECT order_type, COUNT(CASE WHEN is_shipped THEN 1 END) AS shipped, COUNT(CASE WHEN NOT is_shipped THEN 1 END) AS not_shipped, COUNT(*) - COUNT(is_shipped) AS pending FROM orders GROUP BY order_type;
别误用 BOOL_AND / BOOL_OR 当计数器
BOOL_AND 和 BOOL_OR 是逻辑判断函数,不是计数工具。它们返回单个布尔值,不是数量。强行用它们“反推”真假数量,既绕路又易错。
-
BOOL_AND(status)返回TRUE仅当全部为TRUE;只要有一个FALSE或全NULL,就返回FALSE(手册明确:“otherwise false”) -
BOOL_OR(status)返回TRUE只要有一个TRUE;全FALSE或全NULL返回FALSE - 想靠
BOOL_AND判断“有没有 FALSE”是错的——它根本不告诉你 FALSE 有几个,只告诉你“能不能保证全是 TRUE”
用 SUM 替代 COUNT(CASE) 的风险点
有人写 SUM(CASE WHEN status THEN 1 ELSE 0 END) 算 TRUE 数量,看起来简洁,但隐患明显:
- 如果漏写
ELSE 0,CASE对 FALSE 和 NULL 都返回 NULL,SUM会忽略所有 NULL,结果变成只统计 TRUE —— 表面没错,但语义模糊,维护者看不出你是否故意忽略 FALSE - 若字段类型不是
boolean(比如是integer字段存 0/1),status THEN 1会把 0 当 FALSE、非 0 当 TRUE,但 2、-5 这些值也会被当 TRUE,逻辑可能意外偏移 - 相比
COUNT(CASE WHEN ... THEN 1 END),SUM写法在可读性和意图传达上弱一个层级
GROUP BY 粒度决定统计范围,这点常被忽略
所有布尔值统计都依赖 GROUP BY 的分组键。没写 GROUP BY 就是全表聚合;写了就得确保分组键能覆盖你要分析的维度。
- 例如查“每个用户最近 7 天操作是否全成功”,必须
GROUP BY user_id,否则BOOL_AND(success)只返回一个全表结果 - 若 WHERE 过滤了时间范围,但 GROUP BY 错写成
GROUP BY created_at::date,那统计的是“每天内是否全成功”,而非“每人是否全成功”——粒度错位,结果完全失真 - NULL 值在分组内行为一致:不参与
COUNT(status),也不影响BOOL_AND的计算逻辑,但会改变分母(总行数),这点在算比例时必须显式处理











