date
related_level
slug
type
relate_date
summary
status
tags
category
last_updated
Nov 10, 2025 10:03 PM
是否已更新
orginal_page
是否推荐
参考资料
MSSQL
MySQL
PostgreSQL
概要
- 窗口函数对一系列连续数据执行具体的操作,而OVER子句为其划分了这些操作的操作范围,从而让窗口函数可以接受特定范围的连续数据,如从分区首行到当前行、从当前行到分区末行、从当前行前几行到后几行等,进而在SQL中实现移动平均、累积分布等相对复杂的统计功能。
- OVER子句一般分成三部分:分区
PARTITION BY、排序ORDER BY以及窗框区间(frame),其中排序与SELECT语句的排序基本没有差异。其执行顺序也与该顺序一致:先分区再排序,最后确定每个窗口函数的窗框区间后将其中的数据传递给窗口函数。
区分 OVER 子句与 GROUP BY 子句
- GROUP BY子句将结果集划分为多个行组,并在每个组上执行一个或多个聚合函数
- OVER子句在对特定数据集进行分区、排序、划分窗框区间(window frame extent)后对特定范围的连续行执行某个窗口函数
- 两者的相似之处在于:GROUP BY子句和OVER子句都能对结果集进行划分
- 但GROUP BY子句的划分功能要弱于OVER子句,毕竟OVER子句还能在执行排序后进一步划分窗框区间
- 两者的差异之处在于:GROUP BY对整个结果集进行划分并去重,而OVER子句则不然
- 我们使用GROUP BY经常遇到的一个问题就是某列未包含在GROUP BY中而报错,这是因为GROUP BY要对整个数据集进行分组,不能存在未包含在内的数据;而OVER子句不会产生类似的问题,因为其不会对整个结果集进行处理,只会按照OVER子句中的定义对需要的数据集进行处理并返回对应结果。
- 一般来说,GROUP BY子句实现的功能可以通过OVER子句加DISTINCT的方式实现,但OVER子句实现的一些复杂功能并不能通过GROUP BY子句实现
SQL 语法
SQL Server
开窗函数(排名函数、分析函数和聚合函数等)
函数类型 | 函数名 |
排名函数 | |
排名函数 | |
排名函数 | |
排名函数 | |
分析函数 | |
分析函数 | |
分析函数 | |
分析函数 | |
分析函数 | |
分析函数 | |
分析函数 | |
分析函数 | |
聚合函数 | APPROX_COUNT_DISTINCT |
聚合函数 | AVG |
聚合函数 | CHECKSUM_AGG |
聚合函数 | COUNT |
聚合函数 | COUNT_BIG |
聚合函数 | GROUPING |
聚合函数 | GROUPING_ID |
聚合函数 | MAX |
聚合函数 | MIN |
聚合函数 | STDEV |
聚合函数 | STDEVP |
聚合函数 | STRING_AGG |
聚合函数 | SUM |
聚合函数 | VAR |
聚合函数 | VARP |
序列号生成 |
OVER语句的核心语法
语法参数详解
- 三大参数(PARTITION BY、ORDER BY、ROW/RANGE)
RANGE上下限(window frame extent)
- 开窗函数在其
OVER子句中具有以下参数: - value_expression 只能引用可供 FROM 子句使用的列。
- value_expression 不能引用选择列表中的表达式或别名。
- value_expression 可以是列表达式、标量子查询、标量函数或用户定义的变量。
- 如果未指定 PARTITION BY,则此函数将查询结果集的所有行视为单个分区。 如果未指定
ORDER BY子句,则将对分区中的所有行应用函数。 - order_by_expression 只能引用可供 FROM 子句使用的列。 不能将整数指定为表示列名或别名。
- COLLATE 仅适用于 char、nchar、varchar 和 nvarchar 类型的列 。
collation_name既可以是 Windows 排序规则名称,也可以是 SQL 排序规则名称。- 有关详细信息,请参阅 排序规则和 Unicode 支持
- 如果未指定,则默认顺序为
ASC,且开窗函数将使用分区中的所有行。 - 如果指定了此参数,但未指定 ROWS/RANGE,则默认值
RANGE UNBOUNDED PRECEDING AND CURRENT ROW将由可接受可选 ROWS/RANGE 规范(例如min或max)的函数作为窗口帧的默认值。 - 它需要
ORDER BY参数,如果指定了ORDER BY参数,则默认值是从分区起点到当前元素。 - ROWS 子句通过指定当前行之前或之后的固定数目的行,限制分区中的行数。
- RANGE 子句通过指定针对当前行中的值的某一范围的值,从逻辑上限制分区中的行数。
- UNBOUNDED PRECEDING 只能指定为窗口起点。
UNBOUNDED FOLLOWING只能指定为窗口终点。RANGE BETWEENCURRENT ROWANDUNBOUNDED FOLLOWING定义以当前行开始、以分区的最后一行结束的窗口。- 在与 ROWS 一起使用时指定窗口在当前行开始或结束,或者在与 RANGE 一起使用时指定当前值。 CURRENT ROW 可指定为既是起点,又是终点。
PARTITION BY:将查询结果集分为多个分区。
value_expression:指定行集按其分区的列。
ORDER BY:定义结果集的每个分区中行的逻辑顺序。
order_by_expression:指定用于进行排序的列或表达式。
COLLATE collation_name:指定应该根据在 collation_name 中指定的排序规则执行 ORDER BY 操作。
ROWS/RANGE:通过指定分区中的起点和终点来限制分区中的行数。
UNBOUNDED PRECEDING:指定窗口在分区中的第一行开始。
UNBOUNDED FOLLOWING:指定窗口在分区的最后一行结束。
CURRENT ROW:指定窗口在分区中的第一行开始。
- 如果未指定任何参数,则将对整个结果集应用开窗函数。
- 可以在单个查询中将多个开窗函数与单个 FROM 子句一起使用。 每个函数的 OVER 子句在分区和排序上可能不同。
MySQL
窗口函数(必须有OVER语句)
Name | Description |
CUME_DIST() | 值的累积分布 |
DENSE_RANK() | 返回当前行在其分区内的排名,无间隔 |
FIRST_VALUE() | 窗口框架的第一行的参数值 |
LAG() | 当前行在其分区内的向后偏移的行的参数值 |
LAST_VALUE() | 窗口框架的倒数第一行的参数值 |
LEAD() | 当前行在其分区内的向前偏移的行的参数值 |
NTH_VALUE() | 窗口框架的第N行的参数值 |
NTILE() | 当前行在其分区内的桶号 |
PERCENT_RANK() | 百分比排名 |
RANK() | 返回当前行在其分区内的排名,有间隔 |
ROW_NUMBER() | 当前列在其分区内的所在行数 |
除窗口函数外可以用于OVER语句的聚合函数
OVER语句的核心语法
窗口命名(window_name)
- 核心语法
- 可以定义窗口并为其命名,以便在
OVER子句中引用它们。
OVER子句只能将属性添加到命名窗口,不能修改它们 。如果命名窗口定义包含分区、排序或框架属性,则OVER引用窗口名称的子句也不能包含相同类型的属性,否则会发生错误
- 命名窗口间可以向前或向后引用,但不能循环引用
分区语句(partition_clause)
- 分区语句描述了怎么对查询结果行进行分组。
- 窗口函数的分区与数据表的分区有差异
- 对表的分区详见:https://dev.mysql.com/doc/refman/8.0/en/partitioning.html
- 给定行的窗口函数返回结果基于对包含对应行的分区的结果行。
- 核心语法
- 标准SQL只要求分区语句后接列名,但MySQL允许 expr 表达式作为分区基准。
排序语句(order_clause)
- 指示如何对每个分区中的行进行排序
- 根据ORDER BY子句相等的分区行被认为是等价的(peer)
- 如果忽略ORDER BY子句,则分区行无序且所有分区列等价
- ORDER BY语句对每个分区独立生效
- 要对整个结果集进行排序需要在顶层查询中指定ORDER BY语句
- 核心语法
- 如果未显示指定,则ASC为默认选项
- NULL值在ASC中被排序在最前面,在DESC被排序在最后
框架语句(frame_clause)
框架定义
- 框架是在框架子句定义下的当前分区的一个子集
- 框架是相对当前行而确定的,这使得框架可以根据当前行在其分区内的位置在分区内移动
根据SQL标准,部分窗口函数即使定义了框架子句也会取整个分区为对象。
- 核心语法
框架单元(frame_units)
- 确定当前行和框架行之间的关系类型
- 核心语法
ROWS:框架由开始和结束行位置定义。偏移量是行号与当前行号的差异。
RANGE:框架由值范围内的行定义。偏移量是行值与当前行值的差异。
框架区间(frame_extent)
- 确定框架的起点和终点
- 核心语法
frame_start和frame_end的可能取值
- CURRENT ROW
- ROW框架单元下:边界是当前行,
- RANGE框架单元下:边界是当前行的等价行(peers,与当前行相同的行)
- 两者的差异可以参考这篇文章:https://data-xtractor.com/blog/query-builder/range-vs-rows/
- UNBOUNDED PRECEDING
- 边界是第一个分区行
- UNBOUNDED FOLLOWING
- 边界是最后一个分区行
- expr PRECEDING
- ROW框架单元下:边界是当前行之前的 expr 行
- RANGE框架单元下:
- 值非空时,边界为具有等同于与当前行的值减去 expr 表达式后的值的行
- 值为空时,与CURRENT ROW一样,取行的等价行
- 详细样例可见官方文档最后对NULL值处理的补充说明:https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html
- expr 表达式可以是用于参数化查询的参数标记符(?)、非负数值字面量、
INTERVAL val unit形式的时间间隔。 - 需要注意,RANGE对于数值或时间间隔 expr 表达式必须有其各自对应的ORDER BY子句
- expr FOLLOWING
- 类似于 expr PRECEDING
- 但RANGE下,expr 值非空时边界为具有等同于与当前行的值加上 expr 表达式后的值的行
- 在没有框架语句的情况下,默认框架的区间取决于是否存在ORDER BY语句
- 有ORDER BY语句:默认框架等价于
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,相当于从分区首行到当前行,包括当前行的peers - 无ORDER BY语句:默认框架等价于
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,相当于全部分区列
PostgreSQL
窗口函数(必须有OVER语句)
- 基本与MySQL一致,不做翻译
Function | Return Type | Description |
row_number() | bigint | number of the current row within its partition, counting from 1 |
rank() | bigint | rank of the current row with gaps; same as row_number of its first peer |
dense_rank() | bigint | rank of the current row without gaps; this function counts peer groups |
percent_rank() | double precision | relative rank of the current row: (rank - 1) / (total rows - 1) |
cume_dist() | double precision | relative rank of the current row: (number of rows preceding or peer with current row) / (total rows) |
ntile(num_buckets integer) | integer | integer ranging from 1 to the argument value, dividing the partition as equally as possible |
lag(value anyelement [, offset integer [, default anyelement ]]) | same type as value | returns value evaluated at the row that is offset rows before the current row within the partition; if there is no such row, instead return default (which must be of the same type as value). Both offset and default are evaluated with respect to the current row. If omitted, offset defaults to 1 and default to null |
lead(value anyelement [, offset integer [, default anyelement ]]) | same type as value | returns value evaluated at the row that is offset rows after the current row within the partition; if there is no such row, instead return default (which must be of the same type as value). Both offset and default are evaluated with respect to the current row. If omitted, offset defaults to 1 and default to null |
first_value(value any) | same type as value | returns value evaluated at the row that is the first row of the window frame |
last_value(value any) | same type as value | returns value evaluated at the row that is the last row of the window frame |
nth_value(value any, nth integer) | same type as value | returns value evaluated at the row that is the nth row of the window frame (counting from 1); null if no such row |
执行顺序
- 如果包含任何窗口函数,则在GROUP,聚合及HAVING语句执行完毕后再执行窗口函数。
- 当使用多个窗口函数时,所有窗口函数在其窗口定义中具有语法等价的PARTITION BY和ORDER BY子句,这些窗口函数都保证在数据的一次传递中进行计算。因此,即使ORDER BY不能唯一地确定排序,它们也会看到相同的排序。
- 然而,对于具有不同PARTITION BY子句或ORDER BY子句的函数的计算,无法保证排序相同。(在这种情况下,在窗口函数求值过程之间通常需要一个排序步骤,并且排序不能保证保留其顺序被视为等效的行的顺序。)
窗口函数调用核心语法
- 需要注意,
OVER window_name和OVER ( window_definition )是并不完全相等的,后者意味着拷贝并修改窗口定义 - 如果
window_definition引用的窗口包含框架子句则会被拒绝
窗口命名(window_name)
- 窗口命名语法(详见SELECT语句的WINDOW子句)
- 窗口名(window_name)是可以被OVER语句或后续的窗口定义语句所引用的名称
- OVER子句中的(existing_window_name)必须指向WINDOW子句列表中的名称
- 新的窗口将从原窗口中复制ORDER BY和PARTITION BY子句。
- 当且仅当原窗口没有定义ORDER BY语句时,新窗口才能定义自己的ORDER BY语句
- 新窗口必须使用自己的框架语句,被引用的原窗口不能含有框架语句
窗口定义(window_definition)
分区语句(partition_clause)
- 总体上与GROUP BY语句类似,除了其接受参数总是简单表达式而非输出列的名称或序号
- 另外的区别在于其表达式中可以包含聚合函数的调用,因为窗口化总是发生在分组以及聚合之后
排序子句(order_clause)
- 与语句级别的ORDER BY类似,除了其接受参数总是简单表达式而非输出列的名称或序号
框架语句(frame_clause)
- 作为可选项的框架语句为依赖于框架的窗口函数(并不全是)定义了窗口框架
- 窗口框架(window frame)是当前行的关联行集合
- 核心语法
- 默认的框架选项为
RANGE UNBOUNDED PRECEDING - 等价于
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - 这意味着框架区间为从分区首行到当前行的最后一个等价行(peer)
- 等价行(peer)是窗口的ORDER BY子句认为与当前行等价的行
frame_start和frame_end
- 可选项
- 从分区的首行开始
- 忽略RANGE、ROWS、GROUPS的模式定义
- ROWS模式下,offset为整数,框架区间开始于较当前行向前偏移对应数值的行
- GROUPS模式下,offset为整数,框架区间开始于较当前行的等价行组向前偏移对应数值的等价行组
- 等价行组(peer group)是窗口的ORDER BY子句认为相互等价的一组行
- RANGE模式下,框架包含那些已排序的列值不超过当前行已排序列值向前偏移不大于offset值后的值的行
- 必须在窗口定义中显式指定ORDER BY子句
- offset表达式的值类型由排序列决定
- 对于日期时间类型而言,offset为
interval - offset必须非空且非负数
- offset不一定是常量,但不能包含变量、聚合函数或窗口函数
- ROWS模式下意味着开始或结束于当前行
- RANGE或GROUPS模式下意味着开始于当前行的等价行(peer)首行或结束于当前行的等价行末行
- 等价行(peer)是窗口的ORDER BY子句认为与当前行等价的行
- 类似于
offsetPRECEDING,但为向后偏移 - 从分区的倒数第一行开始
- 忽略RANGE、ROWS、GROUPS的模式定义
UNBOUNDED PRECEDING
offset PRECEDING
CURRENT ROW
offset FOLLOWING
UNBOUNDED FOLLOWING
- 如果省略frame_end,则frame_end默认为CURRENT ROW
存在以下限制
- frame_start不能为UNBOUNDED FOLLOWING
- frame_end不能为UNBOUNDED PRECEDING
- 在上面的frame_start和frame_end选项列表中,frame_end选项不能出现在frame_start选项之前
- 如果ORDER BY子句不能产生唯一确定的排序结果,则ROWS模式可能产生不可预测的结果
frame_exclusion
- frame_exclusion允许把在当前行附近的行从框架中排除,即使按照frame_start和frame_end的定义它们应该被纳入框架内
- 可选项
- EXCLUDE CURRENT ROW
- 从框架中排除当前行
- EXCLUDE GROUP
- 从框架中排除当前行及其排序等价行组
- EXCLUDE TIES
- 从框架中排除当前行的等价行,但不排除当前行自身
- EXCLUDE NO OTHERS
- 默认项,不排除当前行及其排序等价行组
SQLite
- 不支持
OVER子句或窗口函数
- Author:白鸟3
- URL:https://blog.kun2peng.top/datasci/sql_window_function
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
