FILTER子句是PostgreSQL和SQLite中专用于聚合函数的语法结构,必须写作“AGG(expr) FILTER (WHERE cond)”,括号不可省略;它从输入行中真正筛选参与聚合的数据,而非将不满足条件的值转为NULL,且不兼容MySQL/SQL Server等数据库。

FILTER 子句不是语法糖,它是语义更明确、执行意图更直接的条件聚合方式。在 PostgreSQL(9.4+)或 SQLite 3.30+ 中,它比 CASE WHEN 更适合做「只对满足条件的行参与聚合」这件事——不是把不满足的转成 NULL 再让聚合函数忽略,而是从输入集里真正筛掉。
FILTER 必须紧贴聚合函数后,且括号不能省
错误写法:AVG(CASE WHEN salary > 10000 THEN salary END) 或 AVG(salary) FILTER WHERE salary > 10000(缺括号)。
正确写法必须是:AVG(salary) FILTER (WHERE salary > 10000)。
这个括号是语法强制要求,不是可选风格;漏掉会直接报错 syntax error at or near "WHERE"。
多个 FILTER 可并列,但不能嵌套或混用窗口函数
你可以在同一查询中写多个 FILTER,比如统计不同分数段人数:COUNT(*) FILTER (WHERE score >= 90)、COUNT(*) FILTER (WHERE score BETWEEN 80 AND 89) 等,它们共享一次表扫描。
但以下情况非法:
-
AVG(salary) FILTER (WHERE dept = 'eng') FILTER (WHERE year > 2020)(不允许嵌套) -
AVG(salary) OVER (PARTITION BY dept) FILTER (WHERE active)(FILTER和OVER不能共存) -
SUM(COUNT(*) FILTER (WHERE status = 'ok'))(不能在聚合函数内部再套聚合 + FILTER)
NULL 值不会被自动跳过,需显式处理
FILTER 的条件求值为 FALSE 或 NULL 时,该行都会被滤除——这是设计行为,不是 bug。
所以如果你写 AVG(score) FILTER (WHERE score > 60),而某行 score 是 NULL,它不会进聚合,但也不会报错。
常见疏忽点:
- 没加
IS NOT NULL导致结果偏低:应写成WHERE score IS NOT NULL AND score > 60 - 误以为
FILTER会像COALESCE那样兜底,其实它完全不碰值本身,只决定“这行参不参与当前这个聚合” - 在
COUNT(*) FILTER场景下,NULL列不影响计数,但条件里若涉及该列,仍要小心空值逻辑
MySQL / SQL Server / Oracle 用户别硬套,它不通用
FILTER 是 PostgreSQL 和 SQLite 特有的(虽源于 SQL:2003 标准,但主流数据库尚未实现)。
在 MySQL 中写 AVG(salary) FILTER (WHERE ...) 会直接报错 ERROR 1064;SQL Server 报 Incorrect syntax near 'FILTER'。
跨库兼容方案只有两种:
- 坚持用
CASE WHEN ... THEN value ELSE NULL END包裹,虽然啰嗦但通用 - 在应用层做分组后过滤,或用 CTE 预先筛数据
FILTER,目前 jOOQ 3.6+ 支持,但 SQLAlchemy、MyBatis 等默认不生成。
最易被忽略的是:FILTER 不改变原始行数,也不影响 GROUP BY 分组逻辑,但它对 NULL 的静默丢弃非常彻底——条件里任何一个列出现 NULL,整行就消失,连“未匹配”都算不上。写之前先想清楚,你到底是要「排除空值」,还是「把空值当 0 处理」,这两者得用不同手段。

















