MySQL 窗口函数

MySQL窗口函数的分类与用法

MySQL 窗口函数总体上可以分为序号函数、分布函数、前后函数、首尾函数和其他函数

目录

MySQL 窗口函数

基本用法格式

基础数据

创建数据表

新增数据

序号函数

ROW_NUMBER

RANK

DENSE_RANK

分布函数

PERCENT_RANK

CUME_DIST

前后函数

LAG

LEAD

首尾函数

FIRST_VALUE

LAST_VALUE

其他函数

NTH_VALUE

NTILE

总结


MySQL 窗口函数

MySQL从8.0版本开始支持窗口函数,其中,窗口可以理解为数据的集合。窗口函数也就是在符合某种条件或者某些条件的记录集合中执行的函数,窗口函数会在每条记录上执行。窗口函数可以分为静态窗口函数和动态窗口函数,其中,静态窗口函数的窗口大小是固定的,不会因为记录的不同而不同;动态窗口函数的窗口大小会随着记录的不同而变化。

窗口函数总体上可以分为序号函数、分布函数、前后函数、首尾函数和其他函数

基本用法格式

如下:

函数名 ([expr]) over子句

over关键字指定函数窗口的范围,如果省略后面括号中的内容,则窗口会包含满足WHERE条件的所有记录,窗口函数会基于所有满足WHERE条件的记录进行计算。

如果over关键字后面的括号不为空,则可以使用如下语法设置窗口。

• window_name:为窗口设置一个别名,用来标识窗口。

• PARTITION BY子句:指定窗口函数按照哪些字段进行分组。分组后,窗口函数可以在每个分组中分别执行。

• ORDER BY子句:指定窗口函数按照哪些字段进行排序。执行排序操作使窗口函数按照排序后的数据记录的顺序进行编号。

• FRAME子句:为分区中的某个子集定义规则,可以用来作为滑动窗口使用。

基础数据

首选创建的t_goods数据例表,并向t_goods数据表中插入数据。

创建数据表
CREATE TABLE `t_goods` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `t_category_id` int(11) DEFAULT NULL,
  `t_category` varchar(30) DEFAULT NULL,
  `t_name` varchar(50) DEFAULT NULL,
  `t_price` decimal(10,2) DEFAULT NULL,
  `t_stock` int(11) DEFAULT NULL,
  `t_upper_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `foreign_category` (`t_category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
新增数据
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('1', '1', '女装/女士精品', 'T恤', '39.90', '1000', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('2', '1', '女装/女士精品', '连衣裙', '79.90', '2500', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('3', '1', '女装/女士精品', '卫衣', '89.90', '1500', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('4', '1', '女装/女士精品', '牛仔裤', '89.90', '3500', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('5', '1', '女装/女士精品', '百褶裙', '29.90', '500', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('6', '1', '女装/女士精品', '呢绒外套', '399.90', '1200', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('7', '2', '户外运动', '自行车', '399.90', '1000', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('8', '2', '户外运动', '山地自行车', '1399.90', '2500', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('9', '2', '户外运动', '登山杖', '59.90', '1500', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('10', '2', '户外运动', '骑行装备', '399.90', '3500', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('11', '2', '户外运动', '运动外套', '799.90', '500', '2025-08-14 00:00:00');
INSERT INTO `t_goods` (`id`, `t_category_id`, `t_category`, `t_name`, `t_price`, `t_stock`, `t_upper_time`) VALUES ('12', '2', '户外运动', '滑板', '499.90', '1200', '2025-08-14 00:00:00');

准备好数据表和新增数据后,

下面就可以针对t_goods表中的数据来验证每个窗口函数的功能。

序号函数

ROW_NUMBER

ROW_NUMBER()函数能够对数据中的序号进行顺序显示。

例如,查询t_goods数据表中每个商品分类下价格最高的3种商品信息。

SQL查询:

SELECT * FROM 
(SELECT ROW_NUMBER() OVER(PARTITION BY t_category_id ORDER BY t_price DESC)
 AS row_num, 
id, t_category_id, t_category, t_name, t_price, t_stock  FROM t_goods) t 
WHERE row_num <= 3;

结果:

在名称为“女装/女士精品”的商品类别中,有两款商品的价格为89.90元,分别是卫衣和牛仔裤。两款商品的序号都应该为2,而不是一个为2,另一个为3。此时,可以使用RANK()函数和DENSE_RANK()函数解决。

RANK

使用RANK()函数能够对序号进行并列排序,并且会跳过重复的序号,比如序号为1、1、3。例如:使用RANK()函数获取t_goods数据表中类别为“女装/女士精品”的价格最高的4款商品信息。

SQL查询:

SELECT * FROM (
SELECT RANK() OVER(
PARTITION BY t_category_id ORDER BY t_price DESC
) AS row_num, id, t_category_id, t_category, t_name, t_price, t_stock
  FROM t_goods
) t WHERE t_category_id = 1 AND row_num <= 4;

结果:

可以看到,使用RANK()函数得出的序号为1、2、2、4,相同价格的商品序号相同,后面的商品序号是不连续的,跳过了重复的序号。

DENSE_RANK

DENSE_RANK()函数对序号进行并列排序,并且不会跳过重复的序号,比如序号为1、1、2。例如,使用DENSE_RANK()函数获取t_goods数据表中类别为“女装/女士精品”的价格最高的4款商品信息。

SQL查询:

SELECT * FROM (
SELECT DENSE_RANK() OVER(
PARTITION BY t_category_id ORDER BY t_price DESC
) AS row_num, id, t_category_id, t_category, t_name, t_price, t_stock 
FROM t_goods
) t WHERE t_category_id = 1 AND row_num <= 3;

结果:

可以看到,使用DENSE_RANK()函数得出的行号为1、2、2、3,相同价格的商品序号相同,后面的商品序号是连续的,并且没有跳过重复的序号。

分布函数

PERCENT_RANK

PERCENT_RANK()函数是等级值百分比函数。

按照如下方式进行计算

 (rank - 1) / (rows - 1)

其中,rank的值为使用RANK()函数产生的序号,rows的值为当前窗口的总记录数。

例如:

计算t_goods数据表中名称为“女装/女士精品”的类别下的商品的PERCENT_RANK值。

SQL查询:

SELECT
  RANK() OVER w AS r,
  PERCENT_RANK() OVER w AS pr,
  id, t_category_id, t_category, t_name, t_price, t_stock
  FROM t_goods
  WHERE t_category_id = 1
  WINDOW w AS (PARTITION BY t_category_id ORDER BY t_price DESC);

结果:

CUME_DIST

CUME_DIST()函数主要用于查询小于或等于某个值的比例。

例如,查询t_goods数据表中小于或等于当前价格的比例。

SQL查询:

SELECT
  CUME_DIST() OVER(PARTITION BY t_category_id ORDER BY t_price DESC) AS cd,
  id, t_category, t_name, t_price
  FROM t_goods;

结果:

前后函数

LAG

LAG(expr,n)函数返回当前行的前n行的expr的值。

例如,查询t_goods数据表中前一个商品价格与当前商品价格的差值。

SQL查询:

SELECT id, t_category, t_name, t_price, pre_price, 
  t_price - pre_price AS diff_price
  FROM (
  SELECT  id, t_category, t_name, t_price,
  LAG(t_price,1) OVER w AS pre_price
  FROM t_goods
  WINDOW w AS (PARTITION BY t_category_id ORDER BY t_price)) t;

结果:

LEAD

LEAD(expr,n)函数返回当前行的后n行的expr的值。

例如,查询t_goods数据表中后一个商品价格与当前商品价格的差值。

SQL查询:

SELECT id, t_category, t_name, behind_price, t_price,
  behind_price - t_price AS diff_price
  FROM(
  SELECT  id, t_category, t_name, t_price,
  LEAD(t_price, 1) OVER w AS behind_price
  FROM t_goods
  WINDOW w AS (PARTITION BY t_category_id ORDER BY t_price)) t;

结果:

首尾函数

FIRST_VALUE

FIRST_VALUE(expr)函数返回第一个expr的值。

例如,按照价格排序,查询第1个商品的价格信息。

SQL查询:

SELECT id, t_category, t_name, t_price, t_stock,
  FIRST_VALUE(t_price) OVER w AS first_price
  FROM t_goods
  WINDOW w AS (PARTITION BY t_category_id ORDER BY t_price);

结果:

LAST_VALUE

LAST_VALUE(expr)函数返回最后一个expr的值。

例如,按照价格排序,查询最后一个商品的价格信息。

SQL查询:

SELECT id, t_category, t_name, t_price, t_stock,
  LAST_VALUE(t_price) OVER w AS last_price     
  FROM t_goods
  WINDOW w AS (PARTITION BY t_category_id ORDER BY t_price);

结果:

其他函数

NTH_VALUE

NTH_VALUE(expr,n)函数返回第n个expr的值。

例如,查询t_goods数据表中排名第3和第4的价格信息。

SQL查询:

SELECT id, t_category, t_name, t_price, 
  NTH_VALUE(t_price,2) OVER w AS second_price,
  NTH_VALUE(t_price,3) OVER w AS third_price
  FROM t_goods
  WINDOW w AS (PARTITION BY t_category_id ORDER BY t_price);

结果:

NTILE

NTILE(n)函数将分区中的有序数据分为n个桶,记录桶编号。

例如,将t_goods表中的商品按照价格分为3组。

SQL查询:

SELECT 
  NTILE(3) OVER w AS nt,
  id, t_category, t_name, t_price
  FROM t_goods
  WINDOW w AS (PARTITION BY t_category_id ORDER BY t_price);

结果:

注意:了解更多MySQL窗口函数可以查看官网文档

https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html#function_row-number

总结

MySQL 窗口函数总体上可以分为序号函数、分布函数、前后函数、首尾函数和其他函数

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JSON_L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值