mysql窗口函数(Window Functions)详解

mysql 窗口函数(Window Functions)详解

    • mysql 窗口函数(Window Functions)详解
      • 1. 窗口函数的基本语法
      • 2. 窗口函数与 `GROUP BY` 的区别
      • 3. 常见的窗口函数
        • 3.1 聚合窗口函数
          • 示例:计算每个部门的累计工资
          • 输出示例
        • 3.2 排名窗口函数
          • 示例:为每个部门的员工按工资排名
          • 输出示例
        • 3.3 偏移窗口函数
          • 示例:比较员工的当前工资与前一个月的工资
          • 输出示例
      • 4. 窗口框架(Frame Clause):滑动窗口
        • 4.1 常见框架类型
          • 示例:计算最近3个月的移动平均工资
          • 输出示例
      • 5. 窗口函数的优势
      • 6. 总结

mysql 窗口函数(Window Functions)详解

窗口函数是 SQL 中的一种强大工具,它允许你在不改变原始表行数的情况下对数据进行聚合计算。与传统的 GROUP BY 不同,窗口函数不会将多行合并为一行,而是可以在每一行上应用聚合操作,同时保留原始的行结构。这使得窗口函数非常适合用于复杂的分析任务,例如:

  • 移动平均:计算某个时间段内的平均值。
  • 排名:为每行数据分配一个排名。
  • 累计和:计算到当前行为止的累计总和。
  • 前后行比较:比较当前行与前几行或后几行的数据。

1. 窗口函数的基本语法

窗口函数的通用语法如下:

window_function(expression) OVER (
    [PARTITION BY partition_expression]
    [ORDER BY order_expression]
    [frame_clause]
)
  • window_function:这是你想要应用的聚合函数或排名函数,例如 SUM()AVG()ROW_NUMBER()RANK() 等。
  • expression:要应用于窗口函数的列或表达式。
  • OVER:指定窗口的范围,告诉 SQL 引擎如何划分数据并应用窗口函数。
  • PARTITION BY:可选。类似于 GROUP BY,但它不会减少行数。它将数据划分为多个分区(或“窗口”),并在每个分区内独立应用窗口函数。
  • ORDER BY:可选。指定窗口内数据的排序顺序。这对于需要按顺序处理的函数(如 ROW_NUMBER()LAG())非常重要。
  • frame_clause:可选。定义窗口的“框架”,即窗口函数作用的行范围。你可以指定从当前行向前或向后扩展的行数。

2. 窗口函数与 GROUP BY 的区别

  • GROUP BY:将多行合并为一行,并对每一组数据进行聚合计算。结果集中的行数会减少。

    SELECT department, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department;
    
    • 这条查询会返回每个部门的平均工资,结果集中每一行代表一个部门。
  • 窗口函数:在每一行上应用聚合计算,但不会减少行数。结果集中的行数与原始表相同,只是每一行都包含了一个新的聚合值。

    SELECT id, name, department, salary,
           AVG(salary) OVER (PARTITION BY department) AS avg_salary
    FROM employees;
    
    • 这条查询会返回每个员工的详细信息,并在每一行上添加一个新列 avg_salary,表示该员工所在部门的平均工资。行数与原始表相同,只是多了聚合结果。

3. 常见的窗口函数

3.1 聚合窗口函数

这些函数类似于普通的聚合函数(如 SUM()AVG()COUNT() 等),但它们可以在每一行上应用,并且可以按分区进行计算。

  • SUM():计算窗口内的总和。
  • AVG():计算窗口内的平均值。
  • COUNT():计算窗口内的行数。
  • MIN():计算窗口内的最小值。
  • MAX():计算窗口内的最大值。
  • CUME_DIST():返回一个值在一组值中的累积分布值,即分区中小于或等于当前行值的分区值的百分比。
  • DENSE_RANK():返回当前行在其分区中的排名,没有间隔。相同的值被视为并列,会被分配相同的排名。
  • FIRST_VALUE():返回窗口框架第一行中参数的值。
  • LAG():返回分区中当前行前面第 N 行中参数的值,如果没有这样的行,则返回默认值。
  • LAST_VALUE():返回窗口框架最后一行中参数的值。
  • LEAD():返回分区中当前行后面第 N 行中参数的值,如果没有这样的行,则返回默认值。
  • NTH_VALUE():返回窗口框架中第 N 行中参数的值,如果没有这样的行,则返回 NULL。
  • NTILE():将分区划分为 N 个组(桶),为分区中的每一行分配其桶编号,并返回当前行在其分区中的桶编号。
  • PERCENT_RANK():返回分区中小于当前行值的分区值的百分比,不包括最高值。
  • RANK():返回当前行在其分区中的排名,有间隔。相同的值被视为并列,会被分配相同的排名,但下一个更大的值的排名会比并列的数量高一个。
  • ROW_NUMBER():返回当前行在其分区中的编号。
示例:计算每个部门的累计工资
SELECT id, name, department, salary,
       SUM(salary) OVER (PARTITION BY department ORDER BY hire_date) AS cumulative_salary
FROM employees;
  • 解释
    • PARTITION BY department:将数据按部门分区。
    • ORDER BY hire_date:在每个部门内按入职日期排序。
    • SUM(salary):计算到当前行为止的累计工资。
输出示例
id name department salary cumulative_salary
1 Alice Sales 5000 5000
2 Bob Sales 6000 11000
3 Carol Sales 7000 18000
4 Dave HR 5500 5500
5 Eve HR 6500 12000
3.2 排名窗口函数

这些函数用于为每一行分配一个排名或序号,通常与 PARTITION BYORDER BY 一起使用。

  • ROW_NUMBER():为每一行分配一个唯一的行号,从 1 开始。
  • RANK():为每一行分配一个排名,相同的值会获得相同的排名,但后续排名会跳过。
  • DENSE_RANK():类似于 RANK(),但不会跳过排名。
  • NTILE(n):将数据分成 n 个桶,并为每一行分配一个桶编号。
示例:为每个部门的员工按工资排名
SELECT id, name, department, salary,
       RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank
FROM employees;
  • 解释
    • PARTITION BY department:将数据按部门分区。
    • ORDER BY salary DESC:在每个部门内按工资从高到低排序。
    • RANK():为每个员工分配一个排名,工资相同的员工会获得相同的排名,但后续排名会跳过。
输出示例
id name department salary salary_rank
3 Carol Sales 7000 1
2 Bob Sales 6000 2
1 Alice Sales 5000 3
5 Eve HR 6500 1
4 Dave HR 5500 2
3.3 偏移窗口函数

这些函数允许你访问当前行之前或之后的行数据,常用于比较相邻行之间的差异。

  • LAG():获取当前行之前的某一行的值。
  • LEAD():获取当前行之后的某一行的值。
  • FIRST_VALUE():获取窗口中的第一个值。
  • LAST_VALUE():获取窗口中的最后一个值。
示例:比较员工的当前工资与前一个月的工资

假设有一个 salaries 表,记录了每个月的工资变化。我们可以使用 LAG() 来比较当前月份的工资与前一个月的工资。

SELECT 
    employee_id, 
    salary_date, 
    salary,
    LAG(salary) OVER (PARTITION BY employee_id ORDER BY salary_date) AS previous_salary,
    salary - LAG(salary) OVER (PARTITION BY employee_id ORDER BY salary_date) AS salary_change
FROM salaries;
  • 解释
    • PARTITION BY employee_id:将数据按员工 ID 分区。
    • ORDER BY salary_date:按工资日期排序。
    • LAG(salary):获取前一个月的工资。
    • salary - LAG(salary):计算当前月与前一个月的工资差额。
输出示例
employee_id salary_date salary previous_salary salary_change
1 2024-01-01 5000 NULL NULL
1 2024-02-01 5200 5000 200
1 2024-03-01 5300 5200 100
2 2024-01-01 6000 NULL NULL
2 2024-02-01 6100 6000 100

4. 窗口框架(Frame Clause):滑动窗口

窗口框架允许你进一步控制窗口函数的作用范围。你可以指定窗口函数应该考虑哪些行,例如只考虑当前行及其前几行或后几行。

4.1 常见框架类型
  • ROWS BETWEEN ... AND ...:基于行数来定义窗口框架。
  • RANGE BETWEEN ... AND ...:基于值的范围来定义窗口框架。
  • UNBOUNDED PRECEDING:表示从窗口的起点(第一行)开始。
  • CURRENT ROW:表示当前行。
  • UNBOUNDED FOLLOWING:表示到窗口的终点(最后一行)结束。
示例:计算最近3个月的移动平均工资
SELECT 
    employee_id, 
    salary_date, 
    salary,
    AVG(salary) OVER (
        PARTITION BY employee_id 
        ORDER BY salary_date 
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS moving_avg_3_months
FROM salaries;
  • 解释
    • PARTITION BY employee_id:将数据按员工 ID 分区。
    • ORDER BY salary_date:按工资日期排序。
    • ROWS BETWEEN 2 PRECEDING AND CURRENT ROW:定义窗口框架为当前行及其前两行,即最近3个月的工资。
    • AVG(salary):计算这3个月的平均工资。
输出示例
employee_id salary_date salary moving_avg_3_months
1 2024-01-01 5000 5000
1 2024-02-01 5200 5100
1 2024-03-01 5300 5166.67
2 2024-01-01 6000 6000
2 2024-02-01 6100 6050

5. 窗口函数的优势

  • 灵活性:窗口函数允许你在不改变行数的情况下进行复杂的聚合计算,适用于多种场景,如移动平均、排名、累计和等。
  • 性能优化:窗口函数通常比自连接或子查询更高效,尤其是在处理大量数据时。
  • 易于理解:窗口函数的语法相对直观,尤其是当你已经熟悉 GROUP BYORDER BY 时,学习窗口函数并不困难。

6. 总结

窗口函数是 SQL 中非常强大的工具,尤其适合用于数据分析和报表生成。通过窗口函数,你可以在每一行上应用聚合计算,而不需要改变原始表的行数。常见的窗口函数包括

你可能感兴趣的:(mysql,数据库,窗口函数,移动平均,滑动窗口)