CASE WHEN 必须嵌套在SUM、COUNT等聚合函数中才安全,否则因SQL执行顺序(先GROUP BY后SELECT)会导致报错或结果不可靠;正确写法如SUM(CASE WHEN status='paid' THEN amount ELSE 0 END),COUNT(CASE WHEN status='paid' THEN 1 END)可省略ELSE;PostgreSQL可用FILTER语法简化,但需注意其限制及NULL返回特性。

CASE WHEN 必须套在 SUM、COUNT 里才安全
单独写 CASE WHEN status = 'paid' THEN amount END 在 SELECT 中,99% 会报错或结果不可靠。SQL 执行顺序是先 GROUP BY,再算 SELECT 列;这个表达式既不是分组字段,也没被聚合函数包裹,数据库不知道该取哪一行的值。
典型错误提示:column "status" must appear in the GROUP BY clause or be used in an aggregate function
- 正确写法必须是:
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) -
ELSE 0要显式写——否则SUM会跳过NULL,漏掉本该计入的 0 值行 - 如果只是计数(比如“有多少笔已支付订单”),用
COUNT(CASE WHEN status = 'paid' THEN 1 END),ELSE可省略,因为COUNT天然忽略NULL
PostgreSQL 用 FILTER 更简洁,但不能乱用
FILTER 是 PostgreSQL 9.4+ 的原生语法,比 CASE WHEN 少写一堆括号和 ELSE,但限制多:
- 只能紧跟聚合函数,且必须带括号:
COUNT(*) FILTER (WHERE status = 'active')✅,COUNT(*) FILTER WHERE status = 'active'❌ - 不能引用外部别名,比如
SELECT x AS val FROM t GROUP BY x HAVING COUNT(*) FILTER (WHERE val > 0)会报错“列 val 不存在” - 空集时行为不一致:
COUNT(*) FILTER (WHERE false)返回0,但SUM(amount) FILTER (WHERE false)返回NULL,报表里容易误判成“无数据”,要用COALESCE(SUM(...), 0)显式兜底
WHERE 和 CASE 的分工必须理清
WHERE 是提前筛数据,CASE 是在筛完的数据里分类打标。混用会白跑大量行或逻辑错位:
- ✅ 正确:先
WHERE status IN ('paid', 'shipped'),再CASE WHEN type = 'cash' THEN ... - ❌ 错误:写
WHERE status = 'paid',却在CASE里还写WHEN status = 'shipped' THEN ...——这些行早被WHERE过滤掉了,CASE永远不会命中 - 跨时间字段(如同时用
create_time和pay_time)容易因NULL或时序错位出错,建议先用子查询打标,再聚合
多条件并列统计时类型和 NULL 要对齐
同一查询里多个 CASE 并列,每个分支返回的类型必须一致,否则可能隐式转换失败或精度丢失:
- 金额类用
SUM(CASE WHEN ... THEN amount ELSE 0 END),确保所有分支都是数值型 - 计数类用
COUNT(CASE WHEN ... THEN 1 END),不写ELSE,避免把0当有效值计入 - 算平均值时慎用
0:比如AVG(CASE WHEN day_type = 'weekend' THEN sales ELSE 0 END)会把工作日的 0 拉低均值,应改用AVG(CASE WHEN day_type = 'weekend' THEN sales END),让NULL自动被跳过
ELSE 的取值逻辑和 FILTER 返回 NULL 的隐含含义——它不是“零”,而是“未定义”,前端展示或后续计算不加判断就容易出偏差。

















