窗函数 (SQL)

在SQL中，窗函数(window function)或分析函数(analytic function)是一个函数，它使用来自一行或多行的值来为每一行返回一个值。 与之形成对比，聚合函数（英语：Aggregate function）为多行返回单个值。窗口函数有一个OVER子句；任何没有OVER子句的函数都不是窗口函数，而是聚合函数或单行（标量）函数。 例如，这里有一个查询，它使用一个窗口函数来比较每个员工的工资与其部门的平均工资（来自PostgreSQL文档的示例）: SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary; 输出: depname | empno | salary | avg ----------+-------+--------+---------------------- develop | 11 | 5200 | 5020.0000000000000000 develop | 7 | 4200 | 5020.0000000000000000 develop | 9 | 4500 | 5020.0000000000000000 develop | 8 | 6000 | 5020.0000000000000000 develop | 10 | 5200 | 5020.0000000000000000 personnel | 5 | 3500 | 3700.0000000000000000 personnel | 2 | 3900 | 3700.0000000000000000 sales | 3 | 4800 | 4866.6666666666666667 sales | 1 | 5000 | 4866.6666666666666667 sales | 4 | 4800 | 4866.6666666666666667 (10 rows) PARTITION BY子句将行分组，并且该函数分别应用于每个分组。 如果PARTITION BY子句被省略（例如如果我们有一个空的OVER()子句），那么整个结果集（英语：Result set）被视为单个分组。对于此查询，报告的平均工资将是所有行的平均值。 窗口函数在聚合之后进行评估（例如，在 GROUP BY（英语：Group by (SQL)）子句和非窗口聚合函数之后）。 根据PostgreSQL文档，窗函数具有下列之一的语法: function_name ([expression [, expression ... ]]) OVER window_name function_name ([expression [, expression ... ]]) OVER ( window_definition ) function_name ( * ) OVER window_name function_name ( * ) OVER ( window_definition ) 其中function_name包括： 开窗函数 序号函数 ROW_NUMBER：顺序排序——1、2、3 RANK：并列排序，跳过重复序号——1、1、3 DENSE_RANK：并列排序，不跳过重复序号——1、1、2 分布函数 PERCENT_RANK CUME_DIST 前驱后继函数 LAG(expr,n)：返回当前行的前n行的expr的值 LEAD(expr,n)：返回当前行的后n行的expr的值 头尾函数 FIRST_VALUE(expr) LAST_VALUE(expr) 其他函数 NTH_VALUE(expr,n)：返回第n行的expr值 NTILE(n)：将有序数据分为n个桶，记录等级数 所有聚合函数 其中 window_definition具有语法: [ existing_window_name ] [ PARTITION BY expression [, ...] ] [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] [ frame_clause ] frame_clause具有下列之一的语法: { RANGE | ROWS | GROUPS } frame_start [ frame_exclusion ] { RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion ] frame_start与frame_end可以是UNBOUNDED PRECEDING, offset PRECEDING, CURRENT ROW, offset FOLLOWING, UNBOUNDED FOLLOWING. frame_exclusion可以是EXCLUDE CURRENT ROW, EXCLUDE GROUP, EXCLUDE TIES, EXCLUDE NO OTHERS. expression指不包含窗函数调用的任何表达式。 注: 方括号[]指可选子句 圆括号{}指多种选项的集合，选项之间以竖杠|分割 窗函数允许访问恰在当前记录之前和之后的记录的数据。 一个窗函数定义当前行周围具有给定行数的帧或窗，并跨窗对数据集执行计算。 NAME | ------------ Aaron| <-- Preceding (unbounded) Andrew| Amelia| James| Jill| Johnny| <-- 1st preceding row Michael| <-- Current row Nick| <-- 1st following row Ophelia| Zach| <-- Following (unbounded) 在上表中，下一个查询为每一行提取具有前一行和后一行的窗口的值w: SELECT LAG(name, 1) OVER(ORDER BY name) "prev", name, LEAD(name, 1) OVER(ORDER BY name) "next" FROM people ORDER BY name 查询结果为: | PREV | NAME | NEXT | |----------|----------|----------| | (null)| Aaron| Andrew| | Aaron| Andrew| Amelia| | Andrew| Amelia| James| | Amelia| James| Jill| | James| Jill| Johnny| | Jill| Johnny| Michael| | Johnny| Michael| Nick| | Michael| Nick| Ophelia| | Nick| Ophelia| Zach| | Ophelia| Zach| (null)| # 首先创建虚拟的业务员销售数据 CREATE TABLE Sales ( idate date, iname char(2), sales int ); # 向表中插入数据 INSERT INTO Sales VALUES ('2021/1/1', '丁一', 200), ('2021/2/1', '丁一', 180), ('2021/2/1', '李四', 100), ('2021/3/1', '李四', 150), ('2021/2/1', '刘猛', 180), ('2021/3/1', '刘猛', 150), ('2021/1/1', '王二', 200), ('2021/2/1', '王二', 180), ('2021/3/1', '王二', 300), ('2021/1/1', '张三', 300), ('2021/2/1', '张三', 280), ('2021/3/1', '张三', 280); # 查询各月中销售业绩最差的业务员 SELECT * FROM (SELECT month(idate),iname,sales, ROW_NUMBER() OVER(PARTITION BY month(idate) ORDER BY sales) as sales_order FROM Sales) as t WHERE sales_order=1; 求用户连续登录天数 # 首先创建虚拟的用户登录表，并插入数据 create table user_login ( user_id varchar(100), login_time datetime ); insert into user_login values (1,'2020-11-25 13:21:12'), (1,'2020-11-24 13:15:22'), (1,'2020-11-24 10:30:15'), (1,'2020-11-24 09:18:27'), (1,'2020-11-23 07:43:54'), (1,'2020-11-10 09:48:36'), (1,'2020-11-09 03:30:22'), (1,'2020-11-01 15:28:29'), (1,'2020-10-31 09:37:45'), (2,'2020-11-25 13:54:40'), (2,'2020-11-24 13:22:32'), (2,'2020-11-23 10:55:52'), (2,'2020-11-22 06:30:09'), (2,'2020-11-21 08:33:15'), (2,'2020-11-20 05:38:18'), (2,'2020-11-19 09:21:42'), (2,'2020-11-02 00:19:38'), (2,'2020-11-01 09:03:11'), (2,'2020-10-31 07:44:55'), (2,'2020-10-30 08:56:33'), (2,'2020-10-29 09:30:28'); # 第一种情况：查看每位用户连续登陆的情况。包括每位用户连续登录的情况、查看每位用户最大连续登录的天数、查看在某个时间段里连续登录天数超过N天的用户 select user_id, min(login_date) start_date, max(login_date) end_date, count(login_date) days # 计算每位用户连续登录天数 from (select *,date_sub(login_date, interval irank day) idate # 增加辅助列，以判断用户是否连续登录 from (select *,rank() over(partition by user_id order by login_date) irank # 对每个用户的登录日期排序 from (select distinct user_id, date(login_time) login_date from user_login # 数据预处理：由于统计的窗口期是天数，对登录时间字段格式转换为日期然后去重（去掉用户一天内多次登录的情况） ) as a ) as b ) as c group by user_id,idate; # 第二种情况：计算每个用户最大连续登录天数 # 第三种情况：查看给定时间段内连续登录天数≥5天的用户 # 找出相差天数为5的记录 select distinct user_id from (select *,datediff(idate5,login_date)+1 as days # 计算当前登录日期与之后第4次登陆的日期的差值 from (select *,lead(login_date,4) over(partition by user_id order by login_date) idate5 # 获取当前登录日期之后第4次登陆的日期 from user_logrin_date) as a) as b where days = 5; SQL:2003（英语：SQL:2003）引入了窗函数，其后的标准扩展了其功能。 MySQL从8.0开始引入了窗函数。目前支持的语法为： # 开窗函数语法 func_name(<parameter>) OVER([PARTITION BY <part_by_condition>] [ORDER BY <order_by_list> ASC|DESC]) Analytic function concepts in Standard SQL | BigQuery. Google Cloud. [2021-03-23]. （原始内容存档于2022-03-24） （英语）.  Window Functions. sqlite.org. [2021-03-23]. （原始内容存档于2022-11-17）.  3.5. Window Functions. PostgreSQL Documentation. 2021-02-11 [2021-03-23]. （原始内容存档于2022-10-31） （英语）.  4.2. Value Expressions. PostgreSQL Documentation. 2021-02-11 [2021-03-23]. （原始内容存档于2022-10-10） （英语）.  Leis, Viktor; Kundhikanjana, Kan; Kemper, Alfons; Neumann, Thomas. Efficient Processing of Window Functions in Analytical SQL Queries. Proc. VLDB Endow. June 2015, 8 (10): 1058–1069. ISSN 2150-8097. doi:10.14778/2794367.2794375.  Cao, Yu; Chan, Chee-Yong; Li, Jie; Tan, Kian-Lee. Optimization of Analytic Window Functions. Proc. VLDB Endow. July 2012, 5 (11): 1244–1255. ISSN 2150-8097. arXiv:1208.0086 . doi:10.14778/2350229.2350243.  Probably the Coolest SQL Feature: Window Functions. Java, SQL and jOOQ. 2013-11-03 [2017-09-26]. （原始内容存档于2021-06-24） （美国英语）.  Window Functions in SQL - Simple Talk. Simple Talk. 2013-10-31 [2017-09-26]. （原始内容存档于2021-09-22） （美国英语）.  SQL Window Functions Introduction. Apache Drill. （原始内容存档于2022-10-10）.  PostgreSQL: Documentation: Window Functions. www.postgresql.org. [2020-04-04]. （原始内容存档于2022-11-02） （英语）.  Window Functions Overview. MariaDB KnowledgeBase. [2021-03-23]. （原始内容存档于2022-10-15）.