在SQL中，**窗函数**(window function)或**分析函数**(analytic function)是一个函数，它使用来自一行或多行的值来为每一行返回一个值。 与之形成对比，聚合函数（英语：）为多行返回单个值。窗口函数有一个`OVER`子句；任何没有`OVER`子句的函数都不是窗口函数，而是聚合函数或单行（标量）函数。

例子1
---

例如，这里有一个查询，它使用一个窗口函数来比较每个员工的工资与其部门的平均工资（来自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()`子句），那么整个结果集（英语：）被视为单个分组。对于此查询，报告的平均工资将是所有行的平均值。

窗口函数在聚合之后进行评估（例如，在 `GROUP BY`（英语：）子句和非窗口聚合函数之后）。

语法
--

根据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`指不包含窗函数调用的任何表达式。

注:

* 方括号[]指可选子句
* 圆括号{}指多种选项的集合，选项之间以竖杠|分割

例子2
---

窗函数允许访问恰在当前记录之前和之后的记录的数据。 一个窗函数定义当前行周围具有给定行数的帧或窗，并跨窗对数据集执行计算。

```
      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)|
```

例子3
---

```
# 首先创建虚拟的业务员销售数据 
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;

```

例子4
---

求用户连续登录天数

```
 
# 首先创建虚拟的用户登录表，并插入数据 
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（英语：）引入了窗函数，其后的标准扩展了其功能。

MySQL从8.0开始引入了窗函数。目前支持的语法为：

```
# 开窗函数语法 
func\_name(<parameter>) OVER([PARTITION BY <part\_by\_condition>] [ORDER BY <order\_by\_list> ASC|DESC])

```

参考文献
----

1. 1 2 . Google Cloud.  [2021-03-23]. （原始内容存档于2022-03-24） （英语）.
2. ↑ . sqlite.org.  [2021-03-23]. （原始内容存档于2022-11-17）.
3. ↑ . PostgreSQL Documentation. 2021-02-11  [2021-03-23]. （原始内容存档于2022-10-31） （英语）.
4. 1 2 . PostgreSQL Documentation. 2021-02-11  [2021-03-23]. （原始内容存档于2022-10-10） （英语）.
5. ↑  Leis, Viktor; Kundhikanjana, Kan; Kemper, Alfons; Neumann, Thomas.. Proc. VLDB Endow. June 2015, **8** (10): 1058–1069. ISSN 2150-8097. doi:10.14778/2794367.2794375.
6. ↑  Cao, Yu; Chan, Chee-Yong; Li, Jie; Tan, Kian-Lee.. Proc. VLDB Endow. July 2012, **5** (11): 1244–1255. ISSN 2150-8097. arXiv:1208.0086 可免费查阅. doi:10.14778/2350229.2350243.
7. ↑ . Java, SQL and jOOQ. 2013-11-03  [2017-09-26]. （原始内容存档于2021-06-24） （美国英语）.
8. ↑ . Simple Talk. 2013-10-31  [2017-09-26]. （原始内容存档于2021-09-22） （美国英语）.
9. ↑ . Apache Drill. （原始内容存档于2022-10-10）.
10. ↑ . www.postgresql.org.  [2020-04-04]. （原始内容存档于2022-11-02） （英语）.
11. ↑ . MariaDB KnowledgeBase.  [2021-03-23]. （原始内容存档于2022-10-15）.

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
SQL |
| --- |
|  |
| 版本 | 
* SQL-86
* SQL-89
* SQL-92
* SQL:1999（英语：）
* SQL:2003（英语：）
* SQL:2006（英语：）
* SQL:2008（英语：）
* SQL:2011（英语：）
* SQL:2016（英语：）

 |
|  |
| 关键字 | 
* As（英语：）
* Case
* Create
* Delete
* From（英语：）
* Having（英语：）
* Insert
* Join
* Merge（英语：）
* Null
* Order by（英语：）
* Over
* Prepare（英语：）
* Select
* Truncate
* Union（英语：）
* Update
* Where（英语：）
* With（英语：）

 |
|  |
| 相关 | 
* 埃德加·科德
* 关系数据库

 |
|  |
| ISO/IEC SQL部分 | 
* Framework（英语：）
* Foundation（英语：）
* Call-Level Interface（英语：）
* Persistent Stored Modules（英语：）
* Management of External Data（英语：）
* Object Language Bindings（英语：）
* Information and Definition Schemas（英语：）
* SQL Routines and Types for the Java Programming Language（英语：）
* XML-Related Specifications（英语：）

 |

 |