WHERE不能直接过滤窗口函数结果,须用子查询、CTE或QUALIFY(BigQuery/Snowflake等支持);FILTER适用于聚合中条件统计,不用于窗口函数;注意NULL和并列排名对结果的影响。
WHERE 不能直接过滤窗口函数结果,得用子查询或 CTE
窗口函数(如
、
、
)是在 SELECT 阶段计算的,而
子句在逻辑上早于 SELECT 执行,所以你不能写
这类条件来过滤窗口结果——会报错
。
正确做法是把带窗口函数的查询包一层:
用 CTE:先算出带排名/标记的中间结果,再在外层
过滤
用子查询(派生表):同理,SELECT 套一层再加 WHERE
别在窗口函数里硬塞条件(比如
)来“绕过”,那只是改统计口径,不是排除行
示例(排除每个部门薪资最高的那条记录):
用 FILTER 子句替代 CASE + 聚合,更清晰地控制统计范围
如果你的目标是「在聚合统计中跳过某些行」,而不是删整行,
(PostgreSQL 9.4+)比写
更直观、语义更明确,且性能通常更好。
只影响当前聚合函数,不影响其他列或窗口逻辑
不支持 MySQL 或 SQL Server;MySQL 得用
,SQL Server 用
注意
不能用于窗口函数,只适用于
、
、
等聚合函数
比如:统计每个部门「非实习生」的平均薪资:
用 QUALIFY 快速过滤窗口结果(BigQuery / Snowflake / DuckDB 支持)
部分现代 SQL 引擎提供了
子句,专为解决「基于窗口函数结果过滤」的问题,它在逻辑上位于窗口计算之后、最终输出之前,相当于隐式子查询封装。
BigQuery 和 Snowflake 中可直接写:
DuckDB 也支持,但 SQLite、PostgreSQL、MySQL 原生不支持(PostgreSQL 有提案但未落地)
注意
不能引用普通列别名(如
),必须重复写窗口表达式或用括号包裹
示例(Snowflake):
排除行时小心 NULL 和重复值对窗口函数的影响
实际数据里,
值和并列排名常导致“排除失败”——你以为排第一被干掉了,结果因为
在排序中被忽略或排末尾,或者
给多人并列第 1,你用
一删就删掉多个。
排序字段含
?显式加
(PostgreSQL/Oracle)或
判断(MySQL)
要严格取唯一首行?优先用
,别用
或
业务上“最高薪资”可能有并列,是否真要全排除?还是只留一条?得跟需求对齐,不能默认套
一个典型陷阱:
真正想留一个最高者,得用
并补充次级排序(如按
)保确定性。
ROW_NUMBER()RANK()SUM() OVERWHEREWHERE rank = 1column "rank" does not existWHERECOUNT(CASE WHEN ... THEN 1 END) OVER (...)WITH ranked AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
)
SELECT * FROM ranked WHERE rn > 1;FILTERCASE WHEN ... THEN value ENDFILTERSUM(IF(condition, 1, 0))SUM(IIF(...))FILTERAVGCOUNTSUMSELECT dept,
AVG(salary) FILTER (WHERE job_title != 'Intern') AS avg_salary_excl_intern
FROM employees
GROUP BY dept;QUALIFYQUALIFY ROW_NUMBER() OVER (...) = 1QUALIFYAS rnSELECT dept, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
QUALIFY rn > 1;NULLNULLRANK()rn = 1NULLORDER BY col DESC NULLS LASTIS NULLROW_NUMBER()RANK()DENSE_RANK()=1-- 如果 salary 有重复,RANK() 可能返回多个 1,下面语句可能一行不剩
SELECT * FROM (
SELECT *, RANK() OVER (PARTITION BY dept ORDER BY salary DESC) r
FROM employees
) t WHERE r > 1;ROW_NUMBER()id