当前位置: 首页 > news >正文

MySQL高级功能:窗口函数

MySQL高级功能:窗口函数

  1. 窗口函数简介

    核心概念:窗口函数对一组与当前行相关的行执行计算。关键点在于,它不会像 GROUP BY 那样将多行合并为单个输出行,而是为每一行返回一个值,同时保留原始行的所有细节。

    你可以把它想象成:为每一行数据都开一个“窗口”,这个“窗口”定义了计算所基于的数据集(如同行、前几行、后几行、整个分区等),然后在这个窗口上进行计算,并将结果直接附加到当前行上。

  2. 窗口函数基本语法

    SELECTcolumn1,column2,window_function(column3) OVER ([PARTITION BY partition_expression][ORDER BY order_expression [ASC | DESC]][frame_clause]) AS alias_name
    FROM table_name;
    

    核心组成部分解析

    1. window_function: 要使用的窗口函数名称(例如 ROW_NUMBER, SUM, RANK 等)。
    2. OVER 子句: 定义窗口的规则。这是窗口函数的灵魂。
      • PARTITION BY: 类似于 GROUP BY,它将结果集划分为不同的分区(组)。窗口函数会独立地应用于每个分区。如果省略,整个结果集就是一个分区。
      • ORDER BY: 定义分区内数据的排序方式。对于排名函数(如 RANK)和计算累计和的函数(如 SUM)至关重要。它还会影响 frame_clause 的默认行为。
      • frame_clause: 定义当前行所在分区中的一个子集(窗口帧)。语法通常是 ROWS BETWEEN start AND end
        • start/end 可以是:
          • UNBOUNDED PRECEDING:分区的第一行
          • UNBOUNDED FOLLOWING:分区的最后一行
          • n PRECEDING:当前行之前的第 n 行
          • n FOLLOWING:当前行之后的第 n 行
          • CURRENT ROW:当前行
        • 示例ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 表示窗口包含当前行、前一行和后一行。
  3. 常用窗口函数分类及示例

    1. 序号函数 (Ranking Functions):为每一行分配一个唯一的序号或排名。

      • ROW_NUMBER(): 为分区内的每一行分配一个唯一的连续序号(无论值是否相同)。

        -- 为每个部门的员工按销售额排名(同分也不同名)
        SELECT *,ROW_NUMBER() OVER (PARTITION BY department ORDER BY amount DESC) AS dept_sale_rank
        FROM sales;
        
      • RANK(): 分配排名,值相同时排名相同,但会留下空位(例如:1, 2, 2, 4)。

        -- 排名,同分则同名,下一个排名会跳号
        SELECT *,RANK() OVER (ORDER BY amount DESC) AS overall_rank
        FROM sales;
        
      • DENSE_RANK(): 分配排名,值相同时排名相同,且不会留下空位(例如:1, 2, 2, 3)。

        -- 排名,同分则同名,下一个排名不跳号
        SELECT *,DENSE_RANK() OVER (ORDER BY amount DESC) AS `dense_rank`
        FROM sales;
        
    2. 分布函数 (Distribution Functions): 计算每行在其分区中的百分比排名(公式: (rank - 1) / (rows - 1))。

      • NTILE(n): 将分区内的数据分成 n 个大致相等的组,并分配组号。

        -- 将总销售额按金额分为4个桶
        SELECT *,NTILE(4) OVER (ORDER BY amount DESC) AS quartile
        FROM sales;
        
    3. 前后值函数 (Value Functions):访问同一分区中其他行的值。

      • LAG(column, offset, default_value): 返回当前行之前 offset 行的值,若不存在则为default_value

        -- 查看每位员工上一次的销售额
        SELECT employee, sale_date, amount,LAG(amount, 1, 0) OVER (PARTITION BY employee ORDER BY sale_date) AS prev_sale_amount
        FROM sales;
        
      • LEAD(column, offset, default_value): 返回当前行之后 offset 行的值,若不存在则为default_value

        -- 查看每位员工下一次的销售额
        SELECT employee, sale_date, amount,LEAD(amount, 1, 0) OVER (PARTITION BY employee ORDER BY sale_date) AS next_sale_amount
        FROM sales;
        
      • FIRST_VALUE(column) / LAST_VALUE(column): 返回窗口帧内的第一个/最后一个值。

        -- 查看每个部门内的最高销售额(显示在每一行)
        SELECT *,FIRST_VALUE(amount) OVER (PARTITION BY department ORDER BY amount DESC) AS dept_highest_sale
        FROM sales;
        
    4. 聚合函数 (Aggregate Functions as Window Functions):所有标准聚合函数(如 SUM, AVG, COUNT, MAX, MIN)都可以用作窗口函数。

      • SUM() 用于计算累计和

        -- 计算每位员工的累计销售额
        SELECT employee, sale_date, amount,SUM(amount) OVER (PARTITION BY employee ORDER BY sale_date) AS running_total
        FROM sales;
        
      • AVG() 用于计算移动平均

        -- 计算每位员工最近3天的平均销售额(包括当天)
        SELECT employee, sale_date, amount,AVG(amount) OVER (PARTITION BY employee ORDER BY sale_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
        FROM sales;
        
  4. 注意事项

    • 版本要求:窗口函数从 MySQL 8.0 版本开始才被支持。如果你使用的是旧版本,将无法利用这些功能。
    • LAST_VALUE()的陷阱:直接使用 LAST_VALUE()可能无法得到分区最后一行,因为其默认窗口范围是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。要获取最后一行,通常需要显式指定框架:LAST_VALUE(expr) OVER (ORDER BY cols RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
  5. 附录

    假设我们有一张salse表:有以下数据

    -- 创建 sales 表
    CREATE TABLE sales (sale_id INT PRIMARY KEY AUTO_INCREMENT,employee VARCHAR(50) NOT NULL,department VARCHAR(50) NOT NULL,sale_date DATE NOT NULL,amount DECIMAL(10, 2) NOT NULL
    );-- 插入示例数据
    INSERT INTO sales (employee, department, sale_date, amount) VALUES
    -- 销售部门数据
    ('Alice', 'Sales', '2023-10-01', 100.00),
    ('Bob', 'Sales', '2023-10-01', 150.00),
    ('Alice', 'Sales', '2023-10-02', 110.00),
    ('Bob', 'Sales', '2023-10-02', 120.00),
    ('Charlie', 'Sales', '2023-10-02', 200.00), -- 新员工
    ('Alice', 'Sales', '2023-10-03', 130.00),
    ('Bob', 'Sales', '2023-10-03', 140.00),
    ('Charlie', 'Sales', '2023-10-03', 180.00),
    ('Alice', 'Sales', '2023-10-04', 120.00), -- 销售额下降
    ('Bob', 'Sales', '2023-10-04', 160.00),
    ('Charlie', 'Sales', '2023-10-04', 220.00), -- 最高销售额-- 市场部门数据
    ('David', 'Marketing', '2023-10-01', 300.00), -- 高价值销售
    ('Eva', 'Marketing', '2023-10-01', 250.00),
    ('David', 'Marketing', '2023-10-02', 280.00),
    ('Eva', 'Marketing', '2023-10-02', 270.00),
    ('Frank', 'Marketing', '2023-10-02', 320.00), -- 新员工,高销售额
    ('David', 'Marketing', '2023-10-03', 310.00),
    ('Eva', 'Marketing', '2023-10-03', 260.00),
    ('Frank', 'Marketing', '2023-10-03', 330.00),
    ('David', 'Marketing', '2023-10-04', 290.00), -- 销售额下降
    ('Eva', 'Marketing', '2023-10-04', 280.00),
    ('Frank', 'Marketing', '2023-10-04', 350.00), -- 最高销售额-- IT部门数据(较少销售记录)
    ('Grace', 'IT', '2023-10-02', 500.00), -- 非常高价值的销售
    ('Henry', 'IT', '2023-10-03', 450.00),
    ('Grace', 'IT', '2023-10-04', 550.00), -- 最高销售额-- 添加一些相同销售额的记录,用于测试排名函数
    ('Ivy', 'Sales', '2023-10-05', 200.00), -- 与Charlie某天销售额相同
    ('Jack', 'Sales', '2023-10-05', 200.00); -- 与Charlie某天销售额相同
    

    现在你可以使用这个数据集测试各种窗口函数:

    1. 部门内销售额排名
    SELECT *,ROW_NUMBER() OVER (PARTITION BY department ORDER BY amount DESC) AS `row_num`,RANK() OVER (PARTITION BY department ORDER BY amount DESC) AS `rank`,DENSE_RANK() OVER (PARTITION BY department ORDER BY amount DESC) AS `dense_rank`
    FROM sales
    ORDER BY department, amount DESC;
    
    1. 员工销售额累计

      SELECT employee, sale_date, amount,SUM(amount) OVER (PARTITION BY employee ORDER BY sale_date) AS running_total
      FROM sales
      ORDER BY employee, sale_date;
      
    2. 部门内移动平均(3天内amount的平均值)

      SELECT department, sale_date, amount,AVG(amount) OVER (PARTITION BY department ORDER BY sale_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
      FROM sales
      ORDER BY department, sale_date;
      
    3. 访问前后行数据

      SELECT employee, sale_date, amount,LAG(amount, 1) OVER (PARTITION BY employee ORDER BY sale_date) AS prev_day_sale,LEAD(amount, 1) OVER (PARTITION BY employee ORDER BY sale_date) AS next_day_sale,amount - LAG(amount, 1) OVER (PARTITION BY employee ORDER BY sale_date) AS day_over_day_change
      FROM sales
      ORDER BY employee, sale_date;
      
    4. 部门内销售额占比

    SELECT employee, department, sale_date, amount,amount / SUM(amount) OVER (PARTITION BY department, sale_date) * 100 AS pct_of_daily_dept_sales
    FROM sales
    ORDER BY department, sale_date, amount DESC;
    
    1. 还有许多应用场景…

文章转载自:

http://ESRaUooK.hhpkb.cn
http://SbPcpH1X.hhpkb.cn
http://13zEEY4o.hhpkb.cn
http://fcjkFkL6.hhpkb.cn
http://EAUl6FK3.hhpkb.cn
http://b2AfviIR.hhpkb.cn
http://jEr9dwMa.hhpkb.cn
http://hEDuIqBf.hhpkb.cn
http://kNTMZXRo.hhpkb.cn
http://OTndWPa2.hhpkb.cn
http://U63l9d5J.hhpkb.cn
http://leyIW2uU.hhpkb.cn
http://m4uhu0Zv.hhpkb.cn
http://K3zYVz6c.hhpkb.cn
http://mi5iMddu.hhpkb.cn
http://ABDWBXLx.hhpkb.cn
http://juWm614q.hhpkb.cn
http://Z3iSoh4O.hhpkb.cn
http://mETaZh8J.hhpkb.cn
http://A4vIRIBB.hhpkb.cn
http://XFp9t0A1.hhpkb.cn
http://Zo4Ngj6e.hhpkb.cn
http://y0i8OuLJ.hhpkb.cn
http://6wXnETjx.hhpkb.cn
http://hDdtZMPp.hhpkb.cn
http://0rVfxOCe.hhpkb.cn
http://TdU3Qrct.hhpkb.cn
http://mmliXF6S.hhpkb.cn
http://1IT2toDI.hhpkb.cn
http://TvaMIUyO.hhpkb.cn
http://www.dtcms.com/a/371877.html

相关文章:

  • 换手率及使用Python获取换手率数据
  • 炉米Lumi:字节跳动推出的AI图像模型分享社区
  • 计算机网络学习(六、应用层)
  • JavaSE 数组从入门到面试全解析
  • 游戏中的设计模式——第二篇 单例模式
  • 【论文阅读】自我进化的AI智能体综述
  • 系统分析师考试备考全面解析
  • 现代C++:C++和现代C++
  • 开始 ComfyUI 的 AI 绘图之旅-图生图之局部重绘(三)
  • 函数合集(1)
  • MySQL高可用方案解析:从复制到云原生
  • Python自学12 — 函数和模块
  • 腾讯云TDSQL-C 与传统MySQL对比
  • docker build命令及参数介绍
  • 老题新解|同行列对角线的格
  • RabbitMQ之死信队列
  • Java面试整理归纳——每日更新
  • Vue2基础概念与指令
  • Android 热点开发的相关api总结
  • 第二章 Python开发环境搭建与工具配置(二)
  • SylixOS 调度浅析
  • 1.TCP/IP模型:各层协议(重点TCP/UDP)
  • 消息推送的三种常见方式:轮询、SSE、WebSocket
  • 【设计模式】 原型模式
  • Apache EnumUtils枚举工具类
  • pycharm如何设置对应的python解释器
  • C++逆向输出一个字符串(三)
  • ZYNQ 定时器
  • Java反射与动态代理学习笔记
  • 实现 SpringBoot 程序加密,禁止 jadx 反编译