OVER() 就是告诉 SQL 要对哪一组行用 window function。 可以理解成,它就是定义“窗口”的方式,让 window function 知道该在哪一块数据上操作。想象一下,一屋子人,我们想统计每一平方米站了多少人。OVER() 就是告诉你要关注房间的哪一部分。换句话说,它就是指定 function 要处理哪一组行。
OVER() 只能和 window functions 一起用,用来对一张或多张表的行做操作,不需要分组。
语法:
window_function() OVER (
[PARTITION BY ...]
[ORDER BY ...]
[ROWS/RANGE ...]
)
这里:
PARTITION BY— 把数据集分成逻辑组ORDER BY— 指定每组内行的顺序ROWS/RANGE— 细化“窗口”大小(比如当前行+1行)
例子:OVER() 不带参数
当 OVER() 不带任何参数时,前面的 function 就会对所有数据行操作。
SELECT
employee_id,
salary,
ROW_NUMBER() OVER () AS row_num -- ROW_NUMBER() 会作用于所有结果行
FROM employees;
发生了什么?
ROW_NUMBER()给每一行分配唯一编号。- 因为
OVER()没有参数,employees表的所有行都当成一个整体处理。
结果:
| employee_id | salary | row_num |
|---|---|---|
| 1 | 50000 | 1 |
| 2 | 60000 | 2 |
| 3 | 55000 | 3 |
用 PARTITION BY 分组
好,现在假设我们想要在每个部门内部编号员工,而不是全公司一起。这时候就要用 PARTITION BY 了。
OVER() 里的 PARTITION BY 会把数据分成组(或者说“分区”)。每组 function 都会单独计算。就像 ROW_NUMBER() 是服务员,他会为每个“桌子”(分区)重新编号。
例子:用 PARTITION BY
SELECT
department_id,
employee_id,
salary,
ROW_NUMBER() OVER (PARTITION BY department_id) AS row_num
FROM employees;
发生了什么?
- 从
employees表里,数据按department_id分组。 - 每组里的行都用
ROW_NUMBER()编号。
结果:
| department_id | employee_id | salary | row_num |
|---|---|---|---|
| 1 | 1 | 50000 | 1 |
| 1 | 3 | 55000 | 2 |
| 2 | 2 | 60000 | 1 |
用 ORDER BY 指定顺序
现在我们来点花样。假如我们想按工资从高到低编号,而不是随便编号。这个就要用 ORDER BY。
ORDER BY 决定 window function 处理行的顺序。
例子:在 OVER() 里用 ORDER BY
SELECT
department_id,
employee_id,
salary,
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank
FROM employees;
发生了什么?
- 数据按组分开(
PARTITION BY department_id)。 - 每组内部按工资降序排(
ORDER BY salary DESC)。 - 每行根据排序分配排名。
结果:
| department_id | employee_id | salary | rank |
|---|---|---|---|
| 1 | 3 | 55000 | 1 |
| 1 | 1 | 50000 | 2 |
| 2 | 2 | 60000 | 1 |
组合多种 window function
SQL 里可以在一个查询里用多个 window function,每个都能有自己的规则。就像一个屋子里同时放音乐又数人头——互不影响!
例子:多个 window function
SELECT
department_id,
employee_id,
salary,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num,
AVG(salary) OVER (PARTITION BY department_id) AS avg_salary
FROM employees;
发生了什么?
ROW_NUMBER()在每组里按工资降序编号。AVG()算每组的平均工资。
结果:
| department_id | employee_id | salary | row_num | avg_salary |
|---|---|---|---|---|
| 1 | 3 | 55000 | 1 | 52500 |
| 1 | 1 | 50000 | 2 | 52500 |
| 2 | 2 | 60000 | 1 | 60000 |
真实生活中的例子
带 OVER() 的 window function 在现实里用得超多。举几个例子:
- 销售分析:每个类别里按销量给商品排名。
- 排名:每个小组里按平均分给学生排位置。
- 时间序列:按时间累计销售额。
销售分析的例子:
SELECT
category_id,
product_id,
product_name,
SUM(sales) OVER (PARTITION BY category_id ORDER BY sales DESC) AS cumulative_sales
FROM products;
用 window function 时常见的坑
- 没写
PARTITION BY
如果你没用 PARTITION BY,window function 就会对整张表操作。这样可能会出乎意料,尤其是你本来想分组的时候。
💡 一定要明确指定表怎么分,比如按用户、订单或类别。
ORDER BY里的数据类型不对
window function 里的 ORDER BY 对数据类型很敏感。如果你按一个存成文本(VARCHAR)的日期字段排序,结果可能是字母顺序,不是时间顺序。
💡 排序前把这些字段转成正确类型(DATE、INTEGER 等)。
ROWS BETWEEN用错了
window function 默认用 ROWS BETWEEN 定义范围。如果你没明确指定,可能会用 RANGE,它的行为不一样,可能返回比你想的更多行。
💡 想精确控制时,用 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,这样能从头累计到当前行。
- 处理
NULL不对
window function 处理 NULL 的方式可能不一样。比如 RANK() 和 DENSE_RANK() 会把 NULL 当成一个值,给它单独排名。
💡 如果你在意 NULL 的位置,可以在 ORDER BY 里用 NULLS LAST 或 NULLS FIRST。
- 用 window 聚合函数代替普通聚合
有时候大家用 window 聚合函数(比如 SUM() OVER(...)),其实用普通的 GROUP BY 聚合就够了,这样反而让查询更复杂、更慢。
💡 只有在你需要保留每一行明细时才用 window function。
GO TO FULL VERSION