postgresql 9.4+ 支持 filter 子句,mysql、sql server、oracle(26ai前)、sqlite 不支持;filter 筛行,case when 映射值;filter 在 group by 后、having 前执行,空集返回 null,需用 coalesce 兜底。

只有 PostgreSQL 9.4+ 支持 FILTER 子句,MySQL、SQL Server、Oracle(26ai 之前)、SQLite(syntax error at or near "FILTER"。别在非 PostgreSQL 环境里调试它。
PostgreSQL 中 FILTER 的合法写法长什么样?
FILTER 不是独立语句,也不是函数参数,它必须紧贴聚合函数之后、且带完整括号。任何位置或括号偏差都会导致解析失败。
- ✅ 正确:
COUNT(*) FILTER (WHERE status = 'paid')、SUM(amount) FILTER (WHERE created_at >= '2024-01-01') - ❌ 报错:
COUNT(*) FILTER WHERE status = 'paid'(漏括号) - ❌ 报错:
COUNT(*) FILTER (WHERE status IN (?))(参数占位符?不被接受) - ❌ 报错:
AVG(salary FILTER (WHERE dept = 'eng'))(误当函数参数嵌套)
FILTER 和 CASE WHEN 到底差在哪?
核心区别:FILTER 是“筛行”,CASE WHEN 是“映射值”。这对 AVG、STDDEV、STRING_AGG 等函数影响直接可见。
-
AVG(score) FILTER (WHERE score > 60):只对 >60 的非 NULL 行计算,分母是这些行数 -
AVG(CASE WHEN score > 60 THEN score END):等效,但靠隐式 NULL 处理,易写成ELSE 0拉低均值 -
STRING_AGG(name, ', ') FILTER (WHERE active):干净利落;而STRING_AGG(CASE WHEN active THEN name END, ', ')在name为 NULL 时可能多出空项或逗号 -
COUNT(*) FILTER (WHERE flag)和COUNT(CASE WHEN flag THEN 1 END)行为一致,但前者无缩进负担、意图一目了然
GROUP BY 场景下容易踩的执行顺序坑
FILTER 发生在 GROUP BY 之后、HAVING 之前。WHERE 先粗筛,FILTER 再细筛每组内部数据——顺序错了,统计就偏了。
- ❌ 错误写法(WHERE 提前过滤掉退款订单,FILTER 失效):
SELECT user_id, AVG(amount) FILTER (WHERE refunded = false)<br>FROM orders WHERE status = 'paid'<br>GROUP BY user_id
- ✅ 正确写法(所有状态保留在 GROUP BY 输入中,FILTER 控制聚合输入):
SELECT user_id,<br> AVG(amount) FILTER (WHERE status = 'paid' AND refunded = false) AS avg_paid_non_refund<br>FROM orders<br>GROUP BY user_id
- 空集时返回值要兜底:
SUM(amount) FILTER (WHERE false)返回NULL,不是0;报表里展示前建议加COALESCE(..., 0)
最常被忽略的是:FILTER 条件中若涉及可能为 NULL 的字段(如 score),不显式写 IS NOT NULL 就会把整行滤掉——这不是 bug,是设计行为。想排除低分但保留空分?得写 WHERE score IS NOT NULL AND score > 60。










