概念
窗口:可以理解为记录集合。
窗口函数:在满足某种条件的记录集合上执行的特殊函数。对于每条记录都要在此窗口内执行函数。有的函数,随着记录不同,窗口大小都是固定的,这种属于静态窗口;有的函数则相反,不同记录对应着不同的窗口,这种动态变化的窗口叫滑动窗口。
窗口函数和聚合函数:聚合函数是多行记录聚合成一行,而窗口函数则是多行聚合为相同的行数,每一行都都会多出一个新列(这个新列就是窗口函数进行运算后的结果)。聚合函数可以作为窗口函数
记住两点:
1,窗口是记录集合,可以是静态窗口,可以是滑动窗口。
2,使用窗口函数后,每一行记录都会使用窗口函数在窗口内(静态窗口或滑动窗口)进行计算产生新的一列。
例子:
新建一张订单表 order_tab
CREATE TABLE `order_tab` (
`order_id` int(11) NOT NULL AUTO_INCREMENT,
`user_no` varchar(100) CHARACTER SET armscii8 COLLATE armscii8_general_ci NOT NULL,
`amount` double(255,0) NOT NULL,
`create_date` datetime NOT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=armscii8
插入一些数据
INSERT INTO order_tab(user_no, amount, create_date) VALUES('001', 100, '2018-01-01 00:00:00');
INSERT INTO order_tab(user_no, amount, create_date) VALUES('001', 300, '2018-01-02 00:00:00');
INSERT INTO order_tab(user_no, amount, create_date) VALUES('001', 500, '2018-01-02 00:00:00');
INSERT INTO order_tab(user_no, amount, create_date) VALUES('001', 800, '2018-01-03 00:00:00');
INSERT INTO order_tab(user_no, amount, create_date) VALUES('001', 900, '2018-01-04 00:00:00');
INSERT INTO order_tab(user_no, amount, create_date) VALUES('002', 500, '2018-01-03 00:00:00');
INSERT INTO order_tab(user_no, amount, create_date) VALUES('002', 600, '2018-01-04 00:00:00');
INSERT INTO order_tab(user_no, amount, create_date) VALUES('002', 300, '2018-01-10 00:00:00');
INSERT INTO order_tab(user_no, amount, create_date) VALUES('002', 800, '2018-01-16 00:00:00');
INSERT INTO order_tab(user_no, amount, create_date) VALUES('002', 800, '2018-01-22 00:00:00');
窗口函数:
- ROW_NUMBER() 分区中的当前行号 查询每个用户最新一笔订单:
SELECT* FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY user_no ORDER BY create_date desc) as row_num,
order_id,
user_no,
amount,
create_date
FROM order_tab
)t WHERE row_num=1;
ROW_NUMBER()后面的OVER是关键字,用来指定窗口函数执行的窗口范围(窗口记录集合),如果括号中什么都不写,则意味着窗口范围是所有行;如果不为空,则支持以下4种语法。
- window_name,给窗口指定一个别名,别名使得代码清晰易读 上面的例子可以使用别名
SELECT* FROM
(
SELECT ROW_NUMBER() OVER w as row_num,
order_id,
user_no,
amount,
create_date
FROM order_tab
WINDOW w AS(PARTITION BY user_no ORDER BY create_date desc)
)t WHERE row_num=1;
-
PARTITION子句 窗口(所有行记录)按照某个字段分组,窗口函数在不同的分组上分别执行。上面的例子就是按照用户id进行分组,分为了两组,在每个用户id分组上,执行从1开始的顺序编号。
-
ORDER BY子句 窗口(所有行记录)按照某个字段进行排序,窗口函数将在排序后的记录上执行,既可以配合PARTITION子句使用,也可以单独使用。
-
FRAME子句 frame是当前分区的一个子集,用来创建滑动窗口。
上面的例子可以看到,创建了两个分组(窗口,数据集),窗口函数分别在两个分组上执行。
使用frame子句,查询每个用户,按照时间倒序,当前订单和前后两个订单的平均金额:
这里,首先按照用户分组形成两个窗口(数据集),按照在两个窗口上使用frame子句形成一个滑动窗口(当前订单和前后两个订单),让聚合函数avg()在这样一个滑动窗口上执行。
滑动窗口的范围指定,有下面两种方式:
- RANK()和DENSE_RANK()函数
和ROW_NUMBER()函数类似,只是在遇到列相同值时,排名序号有所不同。
RANK()会产生相同的序号,同时可能产生序号间隙;DENSE_RANK()也会产生相同的序号,但不会产生序号间隙。
下面这个例子中,用户002按照价格降序排序的时候,前两条记录的amount金额都是800。
以下窗口函数略过,可查阅《深入浅出MySQL 数据库开发 优化与管理维护 第3版》
3. PERCENT_RANK()和CUME_DIST()函数
4. NFILE(N)函数
5. NTH_VALUE(expr, N)函数
6. LAG(expr, N)和LEAD(expr, N)函数
7. FIRST_VALUE(expr)和LAST_VALUE(expr)函数
8. 聚合函数可作为窗口函数
参考书籍:
《深入浅出MySQL 数据库开发 优化与管理维护 第3版》