没写 partition by 时 over() 将全表视为一个分区,row_number() 全局编号,avg() 计算全表均值;order by 决定窗口函数语义,缺省导致错误或未定义行为;rows 按行数、range 按值范围切窗;where 在窗口计算前执行。

OVER() 里不写 PARTITION BY 就是全表当一个分区
很多人以为 OVER() 默认按某种逻辑分组,其实不是——没写 PARTITION BY,就等价于把整张表当作一个大分区。这意味着 ROW_NUMBER() 会从 1 排到总行数,AVG(salary) OVER() 算的是全表平均,而不是按部门或时间滚动。
常见错误现象:SELECT name, salary, AVG(salary) OVER() FROM emp 返回每行都带同一个平均值,但你以为它“自动按部门算了”。
- 想按部门算平均薪资?必须显式写
AVG(salary) OVER(PARTITION BY dept_id) - 想保留原始行顺序再编号?
ROW_NUMBER() OVER(ORDER BY id)可以,但没PARTITION BY就不是“每个部门内重排” - 性能影响:全表窗口(无
PARTITION BY)在大数据量时可能比带分区的更慢,因为无法利用分区剪枝
ORDER BY 在 OVER() 中不是可选的,而是决定“累积行为”的开关
ORDER BY 放在 OVER() 里,不只是为了排序结果,它直接改变窗口函数的语义。比如 SUM(sales) OVER(ORDER BY date) 是累加,而 SUM(sales) OVER(PARTITION BY year ORDER BY date) 是每年内逐日累加。
容易踩的坑:RANK() 或 ROW_NUMBER() 如果漏了 ORDER BY,会报错(PostgreSQL/SQL Server 报错;MySQL 8.0+ 允许但行为未定义,实际返回随机序)。
- 需要“当前行及之前所有行”的聚合?必须带
ORDER BY,否则默认是整窗(ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) - 想算移动平均(比如最近 7 天)?得配合
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW,光靠ORDER BY不够 - 注意 NULL 处理:
ORDER BY col默认把 NULL 排最前(SQL 标准),但不同数据库可配置,MySQL 8.0+ 和 PostgreSQL 行为一致,SQL Server 默认 NULL 最后
ROWS vs RANGE:边界定义方式不同,结果可能差很多
ROWS 按物理行数切窗口,RANGE 按排序值的逻辑范围切。对时间序列或金额类数据,用错会导致计算结果完全偏离预期。
pui 是一款基于jQyery开发的插件库。目前线上稳定使用已有2年多,丰富的接口,简单明了的调用方式,灵活的回调函数,让您轻轻松松打造出富有灵活交互的Web前端界面解决方案。 插件库封装了布局、表单元素、表单校验、弹窗、toast、气泡pop、tab切换、日历时间、分页、表格、树、css命名等功能
典型场景:每日销售额,想算“截止当天的累计和”。如果用 SUM(sales) OVER(ORDER BY sale_date RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),同一天多条记录会被合并进同一窗口;而 ROWS 版本则严格按行序累加,哪怕日期相同也逐行算。
- 日期字段有重复值?优先用
ROWS避免意外聚合 - 需要“值相等即视为同一级”(比如按分数分等级排名),
RANGE更符合语义,但注意它只支持单个ORDER BY表达式且必须是数值或日期类型 - PostgreSQL 支持
RANGE和ROWS,MySQL 8.0+ 仅支持ROWS(RANGE语法存在但被忽略),SQL Server 同样只认ROWS
WHERE 和 OVER() 的执行顺序:过滤发生在窗口计算之后
这是最容易误解的一点:WHERE 条件在窗口函数执行前就已过滤掉数据,所以 OVER(PARTITION BY dept_id) 只会在 WHERE 留下的行中分组,不会“先分组再过滤”。
但如果你写了 HAVING 或想基于窗口结果再过滤(比如只看排名前 3 的人),就不能用 WHERE,得套一层子查询或 CTE。
- 错误写法:
SELECT *, ROW_NUMBER() OVER(PARTITION BY dept_id ORDER BY salary DESC) rn FROM emp WHERE rn —— <code>rn是窗口别名,不能在同级WHERE中引用 - 正确做法:用 CTE 或子查询,例如
SELECT * FROM (SELECT *, ROW_NUMBER() OVER(...) rn FROM emp) t WHERE t.rn - 性能提示:先
WHERE再OVER通常更快;但如果过滤条件依赖窗口结果(如“工资高于部门平均”),就必须两步走,避免全表计算窗口后再丢弃
事情说清了就结束









