MySQL 窗口函数总体上可以分为序号函数、分布函数、前后函数、首尾函数和其他函数
目录
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 窗口函数总体上可以分为序号函数、分布函数、前后函数、首尾函数和其他函数
MySQL窗口函数的分类与用法
418

被折叠的 条评论
为什么被折叠?



