Mysql8.0 推出的强大功能 窗口函数(Window Functions)

🧠 一、什么是窗口函数?

窗口函数是 SQL 中一种在保留原始行的基础上,对行进行分组排序后执行聚合、排名、累计等计算的方法。

与传统的 GROUP BY 聚合不同的是:

👉 窗口函数不会把多行聚成一行,而是为每一行都保留详细信息并加上一个"窗口内"的计算结果。


🧾 二、窗口函数的语法

sql 复制代码
WINDOW_FUNCTION() OVER ( 

    PARTITION BY column1 ORDER BY column2 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 

)

🧱 组成部分详解:

语法部分 说明
WINDOW_FUNCTION() 执行的函数,如 SUM()AVG()ROW_NUMBER()
PARTITION BY 窗口分组,像 GROUP BY,将数据按这个字段分成一个一个"窗口"
ORDER BY 窗口内的排序逻辑,很多函数必须指定排序顺序
ROWS BETWEEN 控制窗口的范围(行数范围)

🧰 三、常见窗口函数

基本上就是我们日常使用的一些函数

函数名 用途
ROW_NUMBER() 每行编号(分组后从1开始)
RANK() / DENSE_RANK() 排名
SUM(col) 累加
AVG(col) 移动平均
LAG(col) / LEAD(col) 前一行/后一行值

🧪 四、实战讲解

4.1 小例子

首先我们先搞一个基础的测试表,类型顺序打乱,

基础需求就是 每一个类型在每一时刻都有一个score,我们始终以id最新的为准,然后我们想要计算,某一时刻,score的累计值 , 表如下:

窗口函数如下

sql 复制代码
WITH test_sum AS(

SELECT id ,
	   TYPE,
	   sum(score) OVER (PARTITION BY TYPE ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS col_score
	   FROM test 
)

SELECT * FROM test_sum;
1. WITH 子句(公用表表达式)

WITH test_sum AS (...) 是一个 公用表表达式 (CTE),它用于定义一个临时的结果集,并将其命名为 test_sum。然后,我们可以在后续的查询中引用这个临时结果集。这个查询的 主要作用 是计算一个分组中的每个记录的滚动平均 score

2. 内部 SELECT 查询的部分解释
sql 复制代码
SELECT id, TYPE, AVG(score) OVER 
(PARTITION BY TYPE ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 
AS col_score FROM test

这部分查询涉及到以下几个部分:

(1)字段选择:
  • idTYPE:查询的字段是 idTYPE,这些字段会出现在最终的结果中。

  • AVG(score)AVG() 是一个聚合函数,用于计算某一列的平均值。在这里它用于计算每个分组内的 score 平均值。

(2)窗口函数 SUM() OVER (...)
  • SUM(score) :计算窗口范围内的 score 总值。

  • OVER (...) :这里的 OVER 关键字表示窗口函数,用于在指定的窗口范围内执行 SUM() 聚合计算。

(3)PARTITION BY TYPE
  • 通过 PARTITION BY TYPE,我们将数据分成不同的 (即按 TYPE 列分组)。每个分组内的数据会独立进行窗口函数的计算。

    • 举例 :如果 TYPE 有值 AB,那么 AVG(score) 会分别计算 TYPE = ATYPE = B 两个分组中的平均值。
(4)ORDER BY id
  • ORDER BY id 指定了每个分组内部的排序规则,按照 id 字段进行排序。排序后,窗口函数会根据这个排序进行计算。

    • 举例 :假设分组后的数据按 id 排序(从小到大),AVG() 会根据这个顺序进行滚动计算。
(5)ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  • ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 定义了窗口的范围:

    • UNBOUNDED PRECEDING:表示从分组的第一行开始。

    • CURRENT ROW:表示窗口的结束是当前行

4.2 计算交易流水

需求 按照id 从小到大, 计算每一个账户的最终 balance ,并体现在该账户id最大的那条数据的balance中 ,期间每一个balance都要 计算出来当时的 balance,credit是加 , debit是减

有了上面的铺垫,所以这里就直接上SQL

sql 复制代码
WITH balance_calculation AS (
    SELECT 
        id,
        account_code,
        credit_amount,
        debit_amount,
        -- 计算余额:按账户分组,按流水创建时间排序,前一行余额 + 当前行的 credit_amount - debit_amount
        SUM(credit_amount - debit_amount) OVER (PARTITION BY account_code ORDER BY create_at  ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS balance
    FROM tbl_posting_line
)
-- 更新最终的 balance
UPDATE tbl_posting_line t
JOIN balance_calculation bc ON t.id = bc.id
SET t.balance = bc.balance;

📌 五、窗口函数 VS 聚合函数

对比点 聚合函数(GROUP BY 窗口函数(OVER(...)
是否保留原始行 ❌ 会合并 ✅ 会保留
适合做什么 汇总统计报表 排名、累计、滑动统计
支持列 限制较多 更灵活
复杂分析 一般 更强大

📦 六、窗口函数常用场景

场景 示例函数
排名 RANK(), ROW_NUMBER()
累计金额 SUM(...) OVER(...)
环比分析 LAG(), LEAD()
分组内排序 ROW_NUMBER()
分组内前N ROW_NUMBER() + WHERE

✅ 七、使用注意事项

  1. 需要 MySQL 8.0+

  2. UPDATE JOIN 中要小心更新逻辑(务必用唯一标识如 id);

  3. OVER() 不能用于 WHERE,但可以用于 CTE 或子查询;

  4. 如果性能是关键,建议先试试窗口函数效率 vs 存储过程。

相关推荐
互联网搬砖老肖3 小时前
运维打铁: MongoDB 数据库集群搭建与管理
运维·数据库·mongodb
典学长编程4 小时前
数据库Oracle从入门到精通!第四天(并发、锁、视图)
数据库·oracle
积跬步,慕至千里5 小时前
clickhouse数据库表和doris数据库表迁移starrocks数据库时建表注意事项总结
数据库·clickhouse
极限实验室5 小时前
搭建持久化的 INFINI Console 与 Easysearch 容器环境
数据库
星辰离彬5 小时前
Java 与 MySQL 性能优化:Java应用中MySQL慢SQL诊断与优化实战
java·后端·sql·mysql·性能优化
白仑色6 小时前
Oracle PL/SQL 编程基础详解(从块结构到游标操作)
数据库·oracle·数据库开发·存储过程·plsql编程
程序猿小D7 小时前
[附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+jsp实现的个人财务管理系统,推荐!
java·数据库·mysql·spring·毕业论文·ssm框架·个人财务管理系统
钢铁男儿8 小时前
C# 接口(什么是接口)
java·数据库·c#
__风__8 小时前
PostgreSQL kv(jsonb)存储
数据库·postgresql
轩情吖9 小时前
Qt的第一个程序(2)
服务器·数据库·qt·qt creator·qlineedit·hello world·编辑框