WINDOW子句必须紧接在FROM(及JOIN)之后、ORDER BY之前声明,是独立语法结构,不可置于WHERE或GROUP BY之后;PostgreSQL完全支持,MySQL 8.0+和SQL Server 2022+部分支持,但作用域仅限当前查询块,不可跨CTE或视图复用。

WINDOW子句必须写在ORDER BY之前,且不能放在WHERE或GROUP BY之后
SQL标准中,WINDOW子句是独立的语法位置,它不属于SELECT列表、WHERE条件或GROUP BY分组逻辑,而是紧接在FROM(及JOIN)之后、ORDER BY之前声明。很多初学者误把它当“辅助语法”随意放置,结果触发ERROR: syntax error at or near "WINDOW"或被忽略——其实数据库根本没解析到它。
正确顺序是:SELECT ... FROM ... [JOIN ...] WINDOW w1 AS (...), w2 AS (...) ORDER BY ...。PostgreSQL完全支持;MySQL 8.0+ 和 SQL Server 2022+ 也支持,但MySQL不支持在VIEW定义中使用WINDOW子句,而PostgreSQL支持。
-
WINDOW定义不可嵌套:不能在OVER (WINDOW w1)里再用WINDOW w2 - 窗口名区分大小写(取决于数据库配置),建议全小写避免歧义
- 同一个查询中可定义多个窗口名,如
WINDOW sales_w AS (PARTITION BY region ORDER BY date), rank_w AS (ORDER BY amount DESC)
复用窗口定义时,OVER (WINDOW name) 必须严格匹配已声明的窗口框架
声明WINDOW w AS (PARTITION BY dept ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)后,后续所有OVER (WINDOW w)都会继承这个完整框架。哪怕你只想用PARTITION BY dept而不需要排序或行范围,也不能“部分复用”——SQL没有“只复用PARTITION BY”的语法。
常见错误是以为OVER (WINDOW w)可以覆盖其中某一部分,比如加ORDER BY name进去,这是非法的:OVER (WINDOW w ORDER BY name)会报错。窗口定义一旦声明,就固定了PARTITION BY、ORDER BY和frame_clause三者。
- 若需不同排序,必须另起一个窗口名,如
WINDOW w_salary AS (... ORDER BY salary), w_name AS (... ORDER BY name) - 若仅需改变frame(如从
ROWS换成RANGE),也必须新命名,无法复用原名 - PostgreSQL允许在
WINDOW子句中引用前面定义的窗口(即“基于已有窗口再扩展”),如WINDOW w1 AS (ORDER BY ts), w2 AS (w1 RANGE BETWEEN INTERVAL '1 day' PRECEDING AND CURRENT ROW),但MySQL不支持这种链式引用
在CTE或子查询里定义WINDOW子句,外部查询无法直接复用
WINDOW子句的作用域仅限于它所在的查询块(query block)。在CTE中写WITH t AS (SELECT ..., ROW_NUMBER() OVER (WINDOW w AS (ORDER BY id)) FROM x) SELECT * FROM t,外部查询不能写ROW_NUMBER() OVER (WINDOW w)——w在外部不可见。
这意味着:想跨层级复用,只能把窗口定义重复写一遍,或改用内联视图(subquery in FROM)并把窗口函数结果作为列输出,再在外层引用该列(但不能再套窗口函数)。
- CTE内部的
WINDOW子句对父查询完全透明 - 视图(CREATE VIEW)中定义的
WINDOW子句同样不对外暴露,调用视图时仍需重新声明 - 唯一能“跨作用域”的方式是:把窗口计算结果固化为列(如
rn = ROW_NUMBER() OVER (...)),然后在外层用普通表达式处理该列
性能上,复用WINDOW名不会减少计算开销,但能降低SQL维护成本
很多人以为WINDOW w AS (...) + 多次OVER (WINDOW w)会让数据库只算一次窗口逻辑,实际并非如此。每个OVER (WINDOW w)仍会独立执行其窗口函数计算(如SUM() OVER w和AVG() OVER w仍是两遍扫描)。复用只是省去了重复写PARTITION BY/ORDER BY的麻烦,并提升可读性和修改一致性。
真正影响性能的是窗口框架本身:无ORDER BY的PARTITION BY通常最快;带ORDER BY且frame为ROWS UNBOUNDED PRECEDING次之;RANGE帧或大时间范围滑动窗口最慢。
- 不要为了“复用”而强行合并逻辑不同的窗口——宁可多定义两个名字,也别硬塞进一个
WINDOW里 - 在EXPLAIN中看不到
WINDOW复用带来的计划差异,它纯粹是语法糖 - 如果发现多个
OVER (WINDOW w)导致慢,瓶颈一定在窗口定义本身(如排序字段无索引),而不是复用机制
实际写的时候,最容易漏掉的是窗口名作用域限制和框架不可覆盖这两点。尤其是从CTE跳到主查询时,下意识想继续用原窗口名,结果语法报错才回头翻文档——这几乎是每个用熟WINDOW的人踩过至少一次的坑。

















