使用窗口函数时,窗口的定义可以包括帧子句。帧是当前分区的子集,帧子句指定如何定义该子集。
帧是相对于当前行确定的,这使得帧可以在分区内移动,取决于当前行在其分区中的位置。例如:
-
通过定义帧为从分区开始到当前行的所有行,可以计算每行的累积总和。
-
通过定义帧为当前行的前后
N
行,可以计算滚动平均值。
以下查询演示了使用移动帧来计算每组时间有序的 level
值的累积总和,以及当前行和其前后行的滚动平均值:
mysql> SELECT
time, subject, val,
SUM(val) OVER (PARTITION BY subject ORDER BY time
ROWS UNBOUNDED PRECEDING)
AS running_total,
AVG(val) OVER (PARTITION BY subject ORDER BY time
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS running_average
FROM observations;
+----------+---------+------+---------------+-----------------+
| time | subject | val | running_total | running_average |
+----------+---------+------+---------------+-----------------+
| 07:00:00 | st113 | 10 | 10 | 9.5000 |
| 07:15:00 | st113 | 9 | 19 | 14.6667 |
| 07:30:00 | st113 | 25 | 44 | 18.0000 |
| 07:45:00 | st113 | 20 | 64 | 22.5000 |
| 07:00:00 | xh458 | 0 | 0 | 5.0000 |
| 07:15:00 | xh458 | 10 | 10 | 5.0000 |
| 07:30:00 | xh458 | 5 | 15 | 15.0000 |
| 07:45:00 | xh458 | 30 | 45 | 20.0000 |
| 08:00:00 | xh458 | 25 | 70 | 27.5000 |
+----------+---------+------+---------------+-----------------+
对于 running_average
列,没有前一个或后一个行的帧行。在这些情况下,AVG()
计算可用的行的平均值。
聚合函数作为窗口函数,操作当前行帧中的行,如同这些非聚合窗口函数:
FIRST_VALUE()
LAST_VALUE()
NTH_VALUE()
标准 SQL 规定,窗口函数应该在整个分区上操作,不应该有帧子句。MySQL 允许对这些函数的帧子句,但忽略它。这些函数使用整个分区,即使指定了帧。
CUME_DIST()
DENSE_RANK()
LAG()
LEAD()
NTILE()
PERCENT_RANK()
RANK()
ROW_NUMBER()
帧子句的语法如下:
frame_clause:
frame_units frame_extent
frame_units:
{ROWS | RANGE}
如果没有帧子句,默认帧取决于是否存在 ORDER BY
子句,如本节后面所述。
The frame_units
值指示当前行和帧行之间的关系:
-
ROWS
:帧是根据开始和结束行位置定义的。偏移量是当前行号与帧行号之间的差异。 -
RANGE
:帧是根据行值范围定义的。偏移量是当前行值与帧行值之间的差异。
The frame_extent
值指示帧的起点和终点。你可以指定帧的起点(在这种情况下,当前行隐式地成为终点),或使用 BETWEEN
指定帧的两个端点:
frame_extent:
{frame_start | frame_between}
frame_between:
BETWEEN frame_start AND frame_end
frame_start, frame_end: {
CURRENT ROW
| UNBOUNDED PRECEDING
| UNBOUNDED FOLLOWING
| expr PRECEDING
| expr FOLLOWING
}
使用 BETWEEN
语法,frame_start
不能晚于 frame_end
。
允许的 frame_start
和 frame_end
值具有以下含义:
-
CURRENT ROW
:对于ROWS
,边界是当前行。对于RANGE
,边界是当前行的同伴。 -
UNBOUNDED PRECEDING
:边界是分区的第一行。 -
UNBOUNDED FOLLOWING
:边界是分区的最后一行。 -
:对于expr
PRECEDINGROWS
,边界是当前行之前的expr
行。对于RANGE
,边界是当前行值减去expr
的行;如果当前行值为NULL
,边界是当前行的同伴。对于
(和expr
PRECEDING
),expr
FOLLOWINGexpr
可以是一个?
参数标记(用于准备语句),一个非负数文字,或者一个时间间隔表达式INTERVAL
。对于val
unit
INTERVAL
表达式,val
指定非负间隔值,unit
是一个关键字,指示值的单位。(有关允许的units
规范的详细信息,请参阅DATE_ADD()
函数的描述在 第 14.7 节,“日期和时间函数”。)RANGE
在数字或时间expr
上需要ORDER BY
在数字或时间表达式上,分别。以下是有效的
和expr
PRECEDING
指示符的示例:expr
FOLLOWING10 PRECEDING INTERVAL 5 DAY PRECEDING 5 FOLLOWING INTERVAL '2:30' MINUTE_SECOND FOLLOWING
-
: 对于expr
FOLLOWINGROWS
,边界是当前行之后的expr
行。对于RANGE
,边界是当前行值加expr
的行;如果当前行值是NULL
,边界是该行的同伴。有关允许的
expr
值,请参阅
的描述。expr
PRECEDING
以下查询演示了 FIRST_VALUE()
、LAST_VALUE()
和两个 NTH_VALUE()
实例:
mysql> SELECT
time, subject, val,
FIRST_VALUE(val) OVER w AS 'first',
LAST_VALUE(val) OVER w AS 'last',
NTH_VALUE(val, 2) OVER w AS 'second',
NTH_VALUE(val, 4) OVER w AS 'fourth'
FROM observations
WINDOW w AS (PARTITION BY subject ORDER BY time
ROWS UNBOUNDED PRECEDING);
+----------+---------+------+-------+------+--------+--------+
| time | subject | val | first | last | second | fourth |
+----------+---------+------+-------+------+--------+--------+
| 07:00:00 | st113 | 10 | 10 | 10 | NULL | NULL |
| 07:15:00 | st113 | 9 | 10 | 9 | 9 | NULL |
| 07:30:00 | st113 | 25 | 10 | 25 | 9 | NULL |
| 07:45:00 | st113 | 20 | 10 | 20 | 9 | 20 |
| 07:00:00 | xh458 | 0 | 0 | 0 | NULL | NULL |
| 07:15:00 | xh458 | 10 | 0 | 10 | 10 | NULL |
| 07:30:00 | xh458 | 5 | 0 | 5 | 10 | NULL |
| 07:45:00 | xh458 | 30 | 0 | 30 | 10 | 30 |
| 08:00:00 | xh458 | 25 | 0 | 25 | 10 | 30 |
+----------+---------+------+-------+------+--------+--------+
每个函数使用当前帧中的行,该帧根据窗口定义从第一个分区行延伸到当前行。对于 NTH_VALUE()
调用,当前帧不总是包含请求的行;在这种情况下,返回值为 NULL
。
如果没有帧子句,默认帧取决于是否存在 ORDER BY
子句:
-
带
ORDER BY
:默认帧包括从分区开始到当前行的所有行,包括当前行的所有同伴(根据ORDER BY
子句等于当前行的行)。默认等同于以下帧规范:RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-
不带
ORDER BY
:默认帧包括所有分区行(因为没有ORDER BY
,所有分区行都是同伴)。默认等同于以下帧规范:RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
因为默认帧取决于 ORDER BY
的存在或不存在,因此添加 ORDER BY
以获取确定性结果可能会改变结果。(例如,SUM()
产生的值可能会改变。)要获得相同的结果但按 ORDER BY
排序,请提供明确的帧规范,以便在 ORDER BY
存在或不存在时使用。
当当前行值为 NULL
时,帧规范的含义可能不是很明显。假设当前行值为 NULL
,以下示例演示了各种帧规范的应用:
-
ORDER BY X ASC RANGE BETWEEN 10 FOLLOWING AND 15 FOLLOWING
帧从
NULL
开始,停止于NULL
,因此仅包括值为NULL
的行。 -
ORDER BY X ASC RANGE BETWEEN 10 FOLLOWING AND UNBOUNDED FOLLOWING
帧从
NULL
开始,停止于分区的末尾。因为ASC
排序将NULL
值放在第一位,因此帧是整个分区。 -
ORDER BY X DESC RANGE BETWEEN 10 FOLLOWING AND UNBOUNDED FOLLOWING
帧从
NULL
开始,停止于分区的末尾。因为DESC
排序将NULL
值放在最后,因此帧仅包括NULL
值。 -
ORDER BY X ASC RANGE BETWEEN 10 PRECEDING AND UNBOUNDED FOLLOWING
帧从
NULL
开始,停止于分区的末尾。因为ASC
排序将NULL
值放在第一位,因此帧是整个分区。 -
ORDER BY X ASC RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING
帧从
NULL
开始,停止于NULL
,因此仅包括值为NULL
的行。 -
ORDER BY X ASC RANGE BETWEEN 10 PRECEDING AND 1 PRECEDING
帧从
NULL
开始,停止于NULL
,因此仅包括值为NULL
的行。 -
ORDER BY X ASC RANGE BETWEEN UNBOUNDED PRECEDING AND 10 FOLLOWING
帧从分区的开始到行值为
NULL
的行。因为ASC
排序将NULL
值放在第一位,因此帧仅包括NULL
值。