mysql8.0的窗口函数的使用详解

1. mysql8.0 的重要更新

建议使用8.0.17及之后的版本,更新的内容比较多,挑几个重要的使用上的更新,其他请参考官网

  • 新增函数索引:之前我们知道,如果在查询中加入了函数计算,索引不生效,就是所谓的索引列上无计算,但在MySQL 8.0.13开始支持在索引中使用函数(表达式)的值。
    • 创建函数索引create index nameIdx on t3( (UPPER(name)) ); 创建一个name列的大写的函数索引
    • 原理:函数索引基于虚拟列功能实现,在MySQL中相当于新增了一个列,这个列会根据你的函数来进行计算结果,然后使用函数索引的时候就会用这个计算后的列作为索引
  • 新增降序索引:MySQL在语法上很早就已经支持降序索引,但实际上创建的仍然是升序索引。mysql8.0开始真正支持降序索引
    • 创建降序索引create index nameAgeIdx on t3( name ,age desc); 创建一个name列升序,age降序的索引
    • 对比:mysql 5.7中通过explain查看也会使用索引,但是Extra字段里有filesort文件排序,但mysql8.0中的Extra字段里没有filesort文件排序,充分利用了降序索引
  • innodb存储引擎 select for update 跳过锁等待:对于select … for share(8.0新增加查询共享锁的语法)或 select … for update, 在语句后面添加NOWAITSKIP LOCKED语法可以跳过锁等待,或者跳过锁定
    • 在5.7及之前的版本: select…for update,如果获取不到锁,会一直等待,直到innodb_lock_wait_timeout超时
    • 在8.0版本: select…for update NOWAIT 通过添加NOWAITSKIP LOCKED语法,能够立即返回。如果查询的行已经加锁,那么NOWAIT会立即报错返回,而SKIP LOCKED也会立即返回,只是返回的结果中不包含被锁定的行。
    • 应用场景比如查询余票记录,如果某些记录已经被锁定,用SKIP LOCKED可以跳过被锁定的记录,只返回没有锁定的记录,提高系统性能
  • group by 不再隐式排序 : mysql 8.0 对于group by 字段不再隐式排序,如需要排序,必须显式加上order by 子句。
  • .frm等元数据存储变动 : MySQL 8.0删除了之前版本的元数据文件,例如表结构.frm等文件,全部集中放入mysql.ibd文件里。
  • 窗口函数:从 MySQL 8.0 开始,新增了一个叫窗口函数的概念,它可以用来实现若干新的查询方式。窗口函数即便分组也不会将多行查询结果合并为一行,而是将结果放回多行当中。下文会对窗口函数做详细的使用介绍

参考文档
mysql8.0 添加弃用和删除的特性
mysql8.0 添加弃用和删除的参数
mysql8.0 InnoDB架构

 

2. 窗口函数的组成部分

窗口函数不会改变原有sql的查询结果,更像是在原有查询的基础上增加一个列,用于计算部分逻辑。作为执行计算的函数,窗口函数主要由三部分组成,如下:

2.1 窗口函数本身

  • ①:排名函数

    • ROW_NUMBER():计算行号。为每一行分配一个唯一的整数,从1开始递增。
    • RANK():计算排名,排名可能不连续。对每一行进行排名,当遇到相同值时,排名会跳过一些数字。
    • DENSE_RANK():计算排名,排名是连续的。对每一行进行排名,但不会跳过排名数字,即使遇到相同值。
       
  • ①:聚合函数

    • MAX(列名):计算窗口中的最大值。
    • MIN(列名):计算窗口中的最小值。
    • SUM(列名):对窗口中的数据求和。
    • AVG(列名):对窗口中的数据求平均值。
    • COUNT(列名):计算窗口中的记录数。
    • STDDEV(列名):计算总体标准差。使用公式 S = sqrt(sum((x_i - x_bar)^2) / N),其中 N 是样本中的数据点数量。
    • STDDEV_SAMP(列名):计算样本标准差。使用公式 S = sqrt(sum((x_i - x_bar)^2) / (N - 1)),其中 N 是样本中的数据点数量。
       
  • ①:偏移函数

    • LAG(列名,行数):取当前行往前(朝分区头部方向)第N行数据的值。
    • LEAD(列名,行数):取当前行往后(朝分区尾部方向)第N行数据的值。
       
  • ①:首尾函数

    • FIRST_VALUE(列名):取窗口内第一行的值。
    • LAST_VALUE(列名):取窗口内最后一行的值。
    • NTH_VALUE(列名,N):取窗口内第N行的值。
       
  • ①:分布函数

    • PERCENT_RANK():计算行的百分比排名。输出百分比格式。
    • CUME_DIST():计算行相对于所有其他行的累积分布。
    • NTILE(N):将数据顺序切分成N等份,返回数据所在等份的编号(从1到N)。

 

2.2 over子句

over子句定义了窗口函数的计算范围。它由三部分组成

  • PARTITION BY:将结果集分成多个独立的分区,每个分区都是一个独立的数据集,窗口函数在每个分区上独立计算。如果省略,整个数据集被视为一个单一的分区。
  • ORDER BY:在每个分区内对行进行排序。
    • 对于排名函数,ORDER BY 子句决定了行的排名顺序。
    • 对于聚合函数,ORDER BY 子句决定了窗口的计算顺序。
  • 窗口框架:窗口框架用于定义在当前分区内,哪些行会被包含在窗口函数的计算中。它必须与ORDER BY子句一起使用(如果没有ORDER BY,则整个分区被视为一个框架),下面介绍一下窗口框架的特性
    • 语法{ROWS | RANGE | GROUPS} BETWEEN frame_start AND frame_end。其中 frame_startframe_end 可以是以下之一

      • UNBOUNDED PRECEDING:分区的第一行
      • UNBOUNDED FOLLOWING:分区的最后一行
      • CURRENT ROW:当前行
      • n PRECEDING:当前行之前的n行(对于ROWS)或值(对于RANGE)
      • n FOLLOWING:当前行之后的n行(对于ROWS)或值(对于RANGE)
      • 注意:n PRECEDING 和 n FOLLOWING中的n必须是非负整数常量
    • 默认窗口框架

      • 如果指定了ORDER BY而没有指定窗口框架,则默认框架为:RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,这意味着窗口函数会计算从分区开始到当前行(包括当前行)的所有行,但是注意,这里使用的是RANGE,所以它会包括所有与当前行在ORDER BY列上具有相同值的行(即并列的行)
      • 如果没有指定ORDER BY,也没有指定窗口框架,则默认框架为:ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,这意味着整个分区都会被包含在窗口内。
    • ROWS 、RANGE 和 GROUPS 的区别
      假设有如下一张表table,则用该表来解释这三者的区别

      id value
      1 10
      2 20
      3 20
      4 30
      • ROWS:基于物理行(行号)移动或后退一行,用行数来指定偏移量,当order by字段出现相同的值时,不考虑并列,每行独立计算。使用 ROWS 的查询sql如下:

        SELECT 
            id, value,
            SUM(value) OVER (ORDER BY value ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows
        FROM table;
        

        使用 ROWS ,每条数据与前后移动一行的值进行sum,结果如下:

        id value sum_rows ​​rows 计算过程​​
        1 10 30 10(当前) + 20(后1行) = 30
        2 20 50 10(前1行) + 20(当前) + 20(后1行) = 50
        3 20 70 20(前1行) + 20(当前) + 30(后1行) = 70
        4 30 50 20(前1行) + 30(当前) = 50
      • RANGE:基于逻辑值(ORDER BY列的值)来增加或减少对应的值,用值来指定偏移量(需要与ORDER BY列类型兼容),当order by字段出现重复的值时,并列的行也加入计算范围,使用 RANGE 的查询sql如下:

        SELECT 
            id, value,
            SUM(value) OVER (ORDER BY value RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING) AS sum_range
        FROM table;
        

        使用 RANGE ,每条数据与 前后 ±10的数据范围所涵盖的记录进行sum当出现并列的重复值时,列入计算范围,结果如下:

        id value sum_range ​​range 计算过程​​
        1 10 50 值范围 [0,20]所涵盖的行 → id1(10), id2(20), id3(20) → 10+20+20=50
        2 20 80 值范围 [10,30]:id1(10), id2(20), id3(20), id4(30) → 10+20+20+30=80
        3 20 80 值范围 [10,30]:id1(10), id2(20), id3(20), id4(30) → 10+20+20+30=80
        4 30 70 值范围 [20,40]:id2(20), id3(20), id4(30) → 20+20+30=70
      • GROUPS(MySQL 8.0.14+):GROUPS介于ROWS和RANGE之间,它按组(一组在ORDER BY列上值相同的行)进行偏移,并列的行会被视为同一组,使用 GROUPS的查询sql如下:

        SELECT 
            value,
            SUM(value) OVER (ORDER BY value GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_groups
        FROM table;
        
        结果:
        value | sum_groups
        10    | 10+20 = 30   -- 当前组(10),前一组没有,后一组(20)所以包括10和20(整个组)
        20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值