PostgreSQL 布尔列改成 false,CASE 统计结果仍不变
CASE 判断了 true、false 常量却没有引用列,导致条件计数不随数据变化。按列使用 FILTER 或 CASE,并核对空值、空集合及关联后的统计口径。
用 SUM(CASE WHEN true THEN 1 ELSE 0 END) 统计布尔列,结果总是总行数;把列里的值改成 false,计数也不变。对应的 CASE WHEN false 则始终得到零。SQL 能执行,是因为它判断了布尔常量,根本没有读取待统计的那一列。
这类错误适用于 PostgreSQL 的普通布尔列。下面用 enabled 作为示例列名,推荐查询使用 PostgreSQL 9.4 起支持的聚合 FILTER;文本里的 "true"、整数状态码等其他类型需先确认业务定义,不能直接照搬。
把每行的判断结果展开
先在本机测试数据库用不创建表的示例观察差异:
SELECT enabled,
CASE WHEN true THEN 1 ELSE 0 END AS constant_result,
CASE WHEN enabled IS TRUE THEN 1 ELSE 0 END AS column_result
FROM (VALUES (true), (false), (NULL::boolean)) AS sample(enabled);
按表达式规则,constant_result 三行都是 1;column_result 依次为 1、0、0。这能区分“聚合算错”与“传给聚合的每行值就错了”。实际查询也可以先取少量已知记录,输出布尔列和 CASE 结果,保留原来的租户、时间等筛选条件。
CASE WHEN 条件 THEN ... 要写完整条件。另一种有效写法是 CASE enabled WHEN true THEN 1 ELSE 0 END,这里在 CASE 后已经指定了要比较的列。不能把两种形式各取一半,写成只判断常量的表达式。
分别统计真、假和空值
如果目标是同一结果里显示三种状态,可以直接给各个计数加过滤条件:
SELECT count(*) FILTER (WHERE enabled IS TRUE) AS true_count,
count(*) FILTER (WHERE enabled IS FALSE) AS false_count,
count(*) FILTER (WHERE enabled IS NULL) AS unknown_count,
count(*) AS total_count
FROM (VALUES (true), (false), (NULL::boolean)) AS sample(enabled);
示例的预期计数为 1、1、1、3。接入业务时,把 FROM 换成真实表或原来的查询输入,并保留所有范围条件。每个 FILTER 只决定对应聚合接收哪些行,不会替其他聚合过滤输入。
SQL 的空值不等于假。IS FALSE 不会把空值计入假值数;若业务明确把“未填写”也视作未启用,应单独采用这个约定,并同步修改字段名称或说明,避免报表读者误以为所有记录都明确保存了 false。
也不要只把原查询换成 count(enabled)。它统计非空值,真和假都会被计入。count(enabled = true) 同样不是“统计表达式为真”:表达式得到非空的 false 时也会计数。
保留 CASE 的查询怎样修改
已有查询不方便改成 FILTER 时,可以让 COUNT 只收到匹配行的非空值:
SELECT count(CASE WHEN enabled IS TRUE THEN 1 END) AS true_count,
count(CASE WHEN enabled IS FALSE THEN 1 END) AS false_count
FROM (VALUES (true), (false), (NULL::boolean)) AS sample(enabled);
省略 ELSE 后,不匹配的行返回 NULL,因此不会进入 COUNT。这里不能补成 ELSE 0,否则零仍是非空值,所有行又都会计入。
若继续使用 SUM(CASE WHEN enabled IS TRUE THEN 1 ELSE 0 END),有输入行时也能累计真值;但整个输入为空时,SUM 返回 NULL,不是零。报表要求空集合显示零时,可明确用 COALESCE(SUM(...), 0)。以上无 GROUP BY 的 COUNT 查询在空输入上返回零;带分组的查询若根本没有组,不会凭空生成一行零值。
用边界数据检查报表
检查全真、全假、真真假混合、含空值以及空输入。采用前面的三状态计数时,在同一输入集合内,三个计数之和应等于总行数。如果还包含关联查询,要确认关联没有把一条业务记录扩成多行:条件聚合统计的是关联后的输入行,不能自动去掉重复记录。
修改只涉及读取表达式,不需要把空值批量改成假,也不需要更改列类型。保存原查询与参数,在报告或接口中对照具体记录和计数后再替换;若下游依赖旧统计口径,应先确认口径变更,必要时回退查询版本。旧计数已经用于后续业务决策时,单独修正 SQL 不会自动纠正那些历史结果。


