Documentation Home
MySQL 8.3 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 Reference Manual  /  ...  /  Window Function Frame Specification

14.20.3 窗口函数帧规范

使用窗口函数时,窗口的定义可以包括帧子句。帧是当前分区的子集,帧子句指定如何定义该子集。

帧是相对于当前行确定的,这使得帧可以在分区内移动,取决于当前行在其分区中的位置。例如:

  • 通过定义帧为从分区开始到当前行的所有行,可以计算每行的累积总和。

  • 通过定义帧为当前行的前后 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_startframe_end 值具有以下含义:

  • CURRENT ROW:对于 ROWS,边界是当前行。对于 RANGE,边界是当前行的同伴。

  • UNBOUNDED PRECEDING:边界是分区的第一行。

  • UNBOUNDED FOLLOWING:边界是分区的最后一行。

  • expr PRECEDING:对于 ROWS,边界是当前行之前的 expr 行。对于 RANGE,边界是当前行值减去 expr 的行;如果当前行值为 NULL,边界是当前行的同伴。

    对于 expr PRECEDING(和 expr FOLLOWING),expr 可以是一个 ? 参数标记(用于准备语句),一个非负数文字,或者一个时间间隔表达式 INTERVAL val unit。对于 INTERVAL 表达式,val 指定非负间隔值,unit 是一个关键字,指示值的单位。(有关允许的 units 规范的详细信息,请参阅 DATE_ADD() 函数的描述在 第 14.7 节,“日期和时间函数”。)

    RANGE 在数字或时间 expr 上需要 ORDER BY 在数字或时间表达式上,分别。

    以下是有效的 expr PRECEDINGexpr FOLLOWING 指示符的示例:

    10 PRECEDING
    INTERVAL 5 DAY PRECEDING
    5 FOLLOWING
    INTERVAL '2:30' MINUTE_SECOND FOLLOWING
  • expr FOLLOWING: 对于 ROWS,边界是当前行之后的 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 值。