filter必须紧贴聚合函数,写成count() filter (where status = 'ok'),括号不可省略,不能嵌套或与窗口函数混用,多个filter可共存且互不影响,空集时除count()返回0外其余返回null。

FILTER必须紧贴聚合函数,不能当独立子句用
直接写 COUNT(*) FILTER WHERE status = 'ok' 会报错 syntax error at or near "WHERE"。它不是独立语法成分,而是聚合函数的修饰子句,必须写成 COUNT(*) FILTER (WHERE status = 'ok')——括号不能省,FILTER 必须紧跟在聚合函数之后、分号之前。
常见误写包括:AVG(salary FILTER (WHERE dept = 'eng'))(把 FILTER 塞进函数参数里)、COUNT(FILTER (WHERE ...))(当成函数调用)。这些都会触发解析失败。
-
FILTER的位置和ORDER BY类似,属于聚合表达式的后缀修饰,不是函数内部逻辑 - 不支持嵌套,比如
SUM(amount) FILTER (WHERE id IN (SELECT id FROM tmp))❌ —— 子查询不被允许 - 条件中不能用占位符
?或绑定变量,只接受列名、常量、标量表达式(如status = 'paid' AND amount > 0)
多个FILTER可共存,但各自独立判断
同一 SELECT 中可以并列多个带不同条件的 FILTER,它们互不影响,也不共享计算过程。例如统计店铺维度的订单总数、成功数、失败数、成功订单平均金额:
SELECT shop, COUNT(*) AS total, COUNT(*) FILTER (WHERE status = 'success') AS success_cnt, COUNT(*) FILTER (WHERE status = 'failed') AS failed_cnt, AVG(payment) FILTER (WHERE status = 'success') AS avg_success FROM order_info WHERE created_at >= '2023-01-01' GROUP BY shop;
注意:AVG(payment) FILTER (WHERE status = 'success') 的分母是满足 status = 'success' 的行数,不是 success_cnt(虽然值一样),更不是 total;且自动跳过 payment 为 NULL 的行。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
- 所有
FILTER共享外层WHERE和GROUP BY,物理扫描只发生一次 - 条件之间无交集或覆盖关系,比如
status = 'success'和status = 'failed'是互斥的,但 PostgreSQL 不做任何合并或优化,纯按行逐个判断 - 若某组内没有任何行匹配某个
FILTER条件,对应聚合结果为NULL(COUNT(*)例外,返回0)
HAVING 中不能直接引用 FILTER 结果别名
HAVING 执行在 FILTER 之后,但不能用 SELECT 列别名过滤。下面写法会报错 “column \"success_cnt\" does not exist”:
SELECT shop, COUNT(*) FILTER (WHERE status = 'success') AS success_cnt FROM order_info GROUP BY shop HAVING success_cnt >= 5;
正确做法是重复条件或用子查询/CTE。推荐写法:
SELECT shop, success_cnt
FROM (
SELECT shop,
COUNT(*) FILTER (WHERE status = 'success') AS success_cnt
FROM order_info
GROUP BY shop
) t
WHERE success_cnt >= 5;
-
FILTER产生的值在HAVING中不可见,因为 SQL 执行顺序是:WHERE → GROUP BY → FILTER → HAVING - 若条件复杂(如需复用),优先用 CTE 预过滤,避免在多处重复写长条件
- 别在
HAVING里写COUNT(*) FILTER (WHERE ...)多次——虽然合法,但可读性差,也容易漏掉括号
空集时返回 NULL,报表场景必须 COALESCE
当 FILTER 条件全不匹配时,SUM、AVG、MAX 等返回 NULL,只有 COUNT(*) 返回 0。这在前端展示时极易被误判为“无数据”,实际是“该条件下无匹配行”。
例如查每个部门薪资在 5k–80k 区间的人均值,若某部门没人落在该区间,AVG(salary) FILTER (WHERE salary BETWEEN 5000 AND 80000) 返回 NULL,不是 0。
- 必须显式用
COALESCE(AVG(...), 0)或COALESCE(SUM(...), 0)转换默认值 -
COUNT(*) FILTER (WHERE false)→0,无需兜底;但COUNT(col) FILTER (...)行为不同(只计非 NULL 的col),要小心区分 - 别依赖
AVG(CASE WHEN ... THEN salary END)自动忽略 NULL 来“替代”FILTER——两者语义一致,但FILTER更直白,且不会因漏写ELSE NULL引入隐式 0
AVG(payment) 本身就会跳过 NULL),两层过滤是正交的——得靠理解聚合函数本身的语义,而不是指望 FILTER 替你兜底。










