window子句必须紧接from之后、where之前,且仅出现一次;错误位置会报语法错,复用时需确保partition by、order by等表达式完全一致且类型严格匹配。

WINDOW子句必须紧接FROM之后、WHERE之前
PostgreSQL要求WINDOW子句只能出现在FROM和WHERE之间,不能塞在SELECT列表后面,也不能放在GROUP BY或ORDER BY之后。写错位置会直接报ERROR: syntax error at or near "WINDOW"。
- ✅ 正确顺序:
SELECT ... FROM t WHERE ... WINDOW w AS (...) - ❌ 错误写法:
SELECT sum(x) OVER w, avg(y) OVER w FROM t WINDOW w AS (...)—— 这里WINDOW虽在FROM后,但WHERE缺失时它仍得紧贴FROM块末尾;若已有WHERE,WINDOW必须在其前 - ⚠️ 注意:即使没
WHERE,也不能把WINDOW写成SELECT ... FROM t WINDOW w AS (...) ORDER BY ...,因为WINDOW必须在ORDER BY之前
命名窗口复用时,ORDER BY和PARTITION BY表达式必须完全一致
一旦定义了WINDOW w AS (PARTITION BY dept ORDER BY ts DESC),所有OVER w都强制继承该逻辑。哪怕你只想要分组计数,不关心排序,PostgreSQL仍会执行排序——带来额外开销。
- 列名必须是原始列,不能是别名:
SELECT dept AS d, COUNT(*) OVER w FROM t WINDOW w AS (PARTITION BY d)会报错,得写PARTITION BY dept - 类型必须严格匹配:如果表中
dept是integer,但你在WINDOW里写了PARTITION BY dept::text,后续OVER w就无法匹配原始列,报column "dept" does not exist in window specification - 排序方向敏感:
ORDER BY ts DESC和ORDER BY ts DESC NULLS LAST在某些场景下不等价,PostgreSQL可能拒绝复用(尤其涉及RANGE帧时)
多个窗口函数共用同一命名窗口,但帧子句不能混用
OVER w复用的是整个窗口定义,包括PARTITION BY、ORDER BY和默认帧。如果某个函数需要自定义帧(比如ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING),就不能直接OVER w,而要显式写出带帧的OVER (w ROWS BETWEEN ...)。
- ✅ 可行:
SELECT ROW_NUMBER() OVER w, SUM(x) OVER w FROM t WINDOW w AS (PARTITION BY a ORDER BY b) - ✅ 带扩展帧:
AVG(x) OVER (w ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - ❌ 不行:
AVG(x) OVER w ROWS BETWEEN ...——ROWS不能脱离OVER()单独写 - ⚠️ 注意:
OVER (w ORDER BY c)是非法的,不能在复用时覆盖ORDER BY;想换排序,必须另起一个窗口名,如w2
别名冲突和跨CTE失效是隐形坑
WINDOW定义的作用域仅限当前查询层级,且别名会和表别名、列名发生命名冲突。
- 如果表别名也是
w,WINDOW w AS (...)在PostgreSQL中会报错;MySQL则可能静默覆盖,导致行为不可预期 - CTE内部无法访问外部查询定义的
WINDOW:WITH t AS (SELECT ...) SELECT * FROM t WINDOW w AS (...)里的w对t无效;CTE里要用窗口,得在CTE内部重新定义 - 真正省资源的复用,是让优化器合并多次窗口计算为一次扫描;但如果只在一个
OVER w里用了一次,其余都手写OVER (...),等于白定义
实际写的时候,最容易被忽略的是PARTITION BY表达式里的隐式类型转换和ORDER BY中NULLS选项的显式声明——它们看着不起眼,但一不一致,窗口就“失联”,查不出错,结果却不对。











