WINDOW子句支持因数据库版本而异:PostgreSQL 8.4+、SQL Server 2005+、MySQL 8.0.2+、SQLite 3.25.0+ 支持,但 SQLite 不支持在 GROUP BY 后复用,仅限 SELECT 和 HAVING 中使用。

WINDOW子句不是所有数据库都支持
PostgreSQL 从 8.4 开始支持 WINDOW 子句,SQL Server 2005+ 支持命名窗口(但语法是 OVER (window_name) 配合 WINDOW 子句),而 MySQL 直到 8.0.2 才引入,SQLite 3.25.0+ 也支持。但 SQLite 的 WINDOW 子句不支持在 GROUP BY 后复用 —— 它只允许出现在 SELECT 列表和 HAVING 中,且不能用于聚合函数的窗口定义。如果你用的是旧版 MySQL 或 MariaDB(ERROR 1064 (42000),别浪费时间调试语法。
正确写法:先声明,再引用
核心是把重复的 PARTITION BY + ORDER BY + FRAME 抽成具名窗口,避免每个函数都写一遍冗长的 OVER (...)。注意:窗口名必须全局唯一,且不能与列名、表别名冲突。
SELECT id, value, AVG(value) OVER w1 AS avg_by_type, SUM(value) OVER w1 AS sum_by_type, ROW_NUMBER() OVER w2 AS rn_desc FROM data WINDOW w1 AS (PARTITION BY category ORDER BY created_at), w2 AS (PARTITION BY category ORDER BY value DESC);
上面这段在 PostgreSQL 和新版 MySQL 中能跑通;但如果你在 WHERE 或 GROUP BY 中试图引用 w1,会失败 —— 窗口定义只作用于窗口函数,不改变行集逻辑。
常见陷阱:ORDER BY 缺失导致帧默认行为突变
当 WINDOW 定义里写了 ORDER BY,但没显式指定 ROWS 或 RANGE,不同数据库默认帧不同:
– PostgreSQL 默认是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
– SQL Server 默认是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
– MySQL 8.0 默认同 PostgreSQL
这意味着同样一个 AVG() OVER w,在没写帧子句时,PostgreSQL 和 MySQL 可能算出和 SQL Server 不同的结果(尤其值重复时)。保险做法是显式写死:w AS (PARTITION BY x ORDER BY y ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
性能提示:命名窗口不减少计算开销
WINDOW 子句只是语法糖,不是执行优化器指令。即使你用同一个窗口名调用了 5 个函数,数据库仍可能对每个函数单独做排序或分组扫描 —— 尤其当涉及不同 ORDER BY 方向(如一个 ASC、一个 DESC)时,无法共享排序结果。真正省资源的方式是:确保多个函数共用完全一致的 PARTITION BY 和 ORDER BY(含方向),并尽量避免混合 RANGE 和 ROWS 帧。
另外,如果窗口定义里用了表达式(比如 PARTITION BY UPPER(tag)),某些数据库不会走索引,而原生列分区可以。这点容易被忽略,但线上查慢时值得回头检查。










