CodeGym /课程 /SQL SELF /OVER() 的语法和主要特点

OVER() 的语法和主要特点

SQL SELF
第 29 级, 课程 2
可用

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;

发生了什么?

  1. ROW_NUMBER() 给每一行分配唯一编号。
  2. 因为 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;

发生了什么?

  1. 从 employees 表里,数据按 department_id 分组。
  2. 每组里的行都用 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;

发生了什么?

  1. 数据按组分开(PARTITION BY department_id)。
  2. 每组内部按工资降序排(ORDER BY salary DESC)。
  3. 每行根据排序分配排名。

结果:

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;

发生了什么?

  1. ROW_NUMBER() 在每组里按工资降序编号。
  2. 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 时常见的坑

  1. 没写 PARTITION BY

如果你没用 PARTITION BY,window function 就会对整张表操作。这样可能会出乎意料,尤其是你本来想分组的时候。

💡 一定要明确指定表怎么分,比如按用户、订单或类别。


  1. ORDER BY 里的数据类型不对

window function 里的 ORDER BY 对数据类型很敏感。如果你按一个存成文本(VARCHAR)的日期字段排序,结果可能是字母顺序,不是时间顺序。

💡 排序前把这些字段转成正确类型(DATE、INTEGER 等)。

  1. ROWS BETWEEN 用错了

window function 默认用 ROWS BETWEEN 定义范围。如果你没明确指定,可能会用 RANGE,它的行为不一样,可能返回比你想的更多行。

💡 想精确控制时,用 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,这样能从头累计到当前行。

  1. 处理 NULL 不对

window function 处理 NULL 的方式可能不一样。比如 RANK() 和 DENSE_RANK() 会把 NULL 当成一个值,给它单独排名。

💡 如果你在意 NULL 的位置,可以在 ORDER BY 里用 NULLS LAST 或 NULLS FIRST。

  1. 用 window 聚合函数代替普通聚合

有时候大家用 window 聚合函数(比如 SUM() OVER(...)),其实用普通的 GROUP BY 聚合就够了,这样反而让查询更复杂、更慢。

💡 只有在你需要保留每一行明细时才用 window function。

2
任务
SQL SELF, 第 29 级, 课程 2
已锁定
表格行号编号
表格行号编号
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION