本节描述了如何使用窗口函数。示例使用与在GROUPING()函数的讨论中相同的销售信息数据集,参见第 14.19.2 节,“GROUP BY 修饰符”:This section describes how to use window functions. Examples use
the same sales information data set as found in the discussion
of the GROUPING() function in
Section 14.19.2, “GROUP BY Modifiers”:
mysql> SELECT * FROM sales ORDER BY country, year, product;
+------+---------+------------+--------+
| year | country | product | profit |
+------+---------+------------+--------+
| 2000 | Finland | Computer | 1500 |
| 2000 | Finland | Phone | 100 |
| 2001 | Finland | Phone | 10 |
| 2000 | India | Calculator | 75 |
| 2000 | India | Calculator | 75 |
| 2000 | India | Computer | 1200 |
| 2000 | USA | Calculator | 75 |
| 2000 | USA | Computer | 1500 |
| 2001 | USA | Calculator | 50 |
| 2001 | USA | Computer | 1500 |
| 2001 | USA | Computer | 1200 |
| 2001 | USA | TV | 150 |
| 2001 | USA | TV | 100 |
+------+---------+------------+--------+
窗口函数对一组查询行执行类似聚合的操作。然而,与将查询行分组为单个结果行的聚合操作不同,窗口函数会为每个查询行生成一个结果:A window function performs an aggregate-like operation on a set of query rows. However, whereas an aggregate operation groups query rows into a single result row, a window function produces a result for each query row:
进行函数计算的行称为当前行。The row for which function evaluation occurs is called the current row.
与当前行相关并对其进行函数计算的查询行组成当前行的窗口。The query rows related to the current row over which function evaluation occurs comprise the window for the current row.
例如,使用销售信息表,这两个查询执行聚合操作,产生所有行作为一个整体的全局总和,以及按国家分组的总和:For example, using the sales information table, these two queries perform aggregate operations that produce a single global sum for all rows taken as a group, and sums grouped per country:
mysql>SELECT SUM(profit) AS total_profitFROM sales;+--------------+ | total_profit | +--------------+ | 7535 | +--------------+ mysql>SELECT country, SUM(profit) AS country_profitFROM salesGROUP BY countryORDER BY country;+---------+----------------+ | country | country_profit | +---------+----------------+ | Finland | 1610 | | India | 1350 | | USA | 4575 | +---------+----------------+
相比之下,窗口操作不会将查询行的组折叠成单个输出行。相反,它们会为每一行生成一个结果。像前面的查询一样,下面的查询使用了SUM(),但这一次作为窗口函数:By contrast, window operations do not collapse groups of query
rows to a single output row. Instead, they produce a result for
each row. Like the preceding queries, the following query uses
SUM(), but this time as a window
function:
mysql>SELECTyear, country, product, profit,SUM(profit) OVER() AS total_profit,SUM(profit) OVER(PARTITION BY country) AS country_profitFROM salesORDER BY country, year, product, profit;+------+---------+------------+--------+--------------+----------------+ | year | country | product | profit | total_profit | country_profit | +------+---------+------------+--------+--------------+----------------+ | 2000 | Finland | Computer | 1500 | 7535 | 1610 | | 2000 | Finland | Phone | 100 | 7535 | 1610 | | 2001 | Finland | Phone | 10 | 7535 | 1610 | | 2000 | India | Calculator | 75 | 7535 | 1350 | | 2000 | India | Calculator | 75 | 7535 | 1350 | | 2000 | India | Computer | 1200 | 7535 | 1350 | | 2000 | USA | Calculator | 75 | 7535 | 4575 | | 2000 | USA | Computer | 1500 | 7535 | 4575 | | 2001 | USA | Calculator | 50 | 7535 | 4575 | | 2001 | USA | Computer | 1200 | 7535 | 4575 | | 2001 | USA | Computer | 1500 | 7535 | 4575 | | 2001 | USA | TV | 100 | 7535 | 4575 | | 2001 | USA | TV | 150 | 7535 | 4575 | +------+---------+------------+--------+--------------+----------------+
查询中的每个窗口操作都通过包含一个OVER子句来表示,该子句指定如何将查询行划分为组,以便由窗口函数处理:Each window operation in the query is signified by inclusion of
an OVER clause that specifies how to
partition query rows into groups for processing by the window
function:
第一个
OVER子句是空的,这将整个查询行集视为单个分区。窗口函数因此生成一个全局总和,但会为每一行生成该总和。The firstOVERclause is empty, which treats the entire set of query rows as a single partition. The window function thus produces a global sum, but does so for each row.第二个
OVER子句按国家划分行,为每个分区(每个国家)生成一个总和。函数为每个分区行生成这个总和。The secondOVERclause partitions rows by country, producing a sum per partition (per country). The function produces this sum for each partition row.
窗口函数仅允许在选择列表和ORDER BY子句中使用。查询结果行是从FROM子句确定的,在WHERE、GROUP BY和HAVING处理之后,而窗口函数的执行发生在ORDER BY、LIMIT和SELECT
DISTINCT之前。Window functions are permitted only in the select list and
ORDER BY clause. Query result rows are
determined from the FROM clause, after
WHERE, GROUP BY, and
HAVING processing, and windowing execution
occurs before ORDER BY,
LIMIT, and SELECT
DISTINCT.
OVER子句允许用于许多聚合函数,因此这些函数可以作为窗口函数或非窗口函数使用,这取决于是否存在OVER子句:The OVER clause is permitted for many
aggregate functions, which therefore can be used as window or
nonwindow functions, depending on whether the
OVER clause is present or absent:
AVG() BIT_AND() BIT_OR() BIT_XOR() COUNT() JSON_ARRAYAGG() JSON_OBJECTAGG() MAX() MIN() STDDEV_POP(), STDDEV(), STD() STDDEV_SAMP() SUM() VAR_POP(), VARIANCE() VAR_SAMP()
有关每个聚合函数的详细信息,请参阅第 14.19.1 节,“聚合函数说明”。For details about each aggregate function, see Section 14.19.1, “Aggregate Function Descriptions”.
MySQL 还支持仅作为窗口函数使用的非聚合函数。对于这些函数,OVER 子句是必需的:MySQL also supports nonaggregate functions that are used only as
window functions. For these, the OVER clause
is mandatory:
CUME_DIST() DENSE_RANK() FIRST_VALUE() LAG() LAST_VALUE() LEAD() NTH_VALUE() NTILE() PERCENT_RANK() RANK() ROW_NUMBER()
有关每个非聚合函数的详细信息,请参见 第 14.20.1 节,“窗口函数描述”。For details about each nonaggregate function, see Section 14.20.1, “Window Function Descriptions”.
作为这些非聚合窗口函数之一的示例,此查询使用了 ROW_NUMBER(),它生成每个分区内每行的行号。在此情况下,每个国家的行都会被编号。默认情况下,分区行是无序的,行号是非确定性的。要对分区行进行排序,请在窗口定义中包含 ORDER BY 子句。该查询使用无序和有序分区(row_num1 和 row_num2 列)来说明省略和包含 ORDER BY 的区别:As an example of one of those nonaggregate window functions,
this query uses ROW_NUMBER(),
which produces the row number of each row within its partition.
In this case, rows are numbered per country. By default,
partition rows are unordered and row numbering is
nondeterministic. To sort partition rows, include an
ORDER BY clause within the window definition.
The query uses unordered and ordered partitions (the
row_num1 and row_num2
columns) to illustrate the difference between omitting and
including ORDER BY:
mysql>SELECTyear, country, product, profit,ROW_NUMBER() OVER(PARTITION BY country) AS row_num1,ROW_NUMBER() OVER(PARTITION BY country ORDER BY year, product) AS row_num2FROM sales;+------+---------+------------+--------+----------+----------+ | year | country | product | profit | row_num1 | row_num2 | +------+---------+------------+--------+----------+----------+ | 2000 | Finland | Computer | 1500 | 2 | 1 | | 2000 | Finland | Phone | 100 | 1 | 2 | | 2001 | Finland | Phone | 10 | 3 | 3 | | 2000 | India | Calculator | 75 | 2 | 1 | | 2000 | India | Calculator | 75 | 3 | 2 | | 2000 | India | Computer | 1200 | 1 | 3 | | 2000 | USA | Calculator | 75 | 5 | 1 | | 2000 | USA | Computer | 1500 | 4 | 2 | | 2001 | USA | Calculator | 50 | 2 | 3 | | 2001 | USA | Computer | 1500 | 3 | 4 | | 2001 | USA | Computer | 1200 | 7 | 5 | | 2001 | USA | TV | 150 | 1 | 6 | | 2001 | USA | TV | 100 | 6 | 7 | +------+---------+------------+--------+----------+----------+
如前所述,要使用窗口函数(或将聚合函数视为窗口函数),需在函数调用后包含一个 OVER 子句。OVER 子句有两种形式:As mentioned previously, to use a window function (or treat an
aggregate function as a window function), include an
OVER clause following the function call. The
OVER clause has two forms:
over_clause: {OVER (window_spec) | OVERwindow_name}
这两种形式都定义了窗口函数应如何处理查询行。它们的不同之处在于窗口是直接在 OVER 子句中定义,还是通过引用查询中其他地方定义的命名窗口提供:Both forms define how the window function should process query
rows. They differ in whether the window is defined directly in
the OVER clause, or supplied by a reference
to a named window defined elsewhere in the query:
在第一种情况下,窗口规范直接出现在
OVER子句的括号中。In the first case, the window specification appears directly in theOVERclause, between the parentheses.在第二种情况下,
window_name是由查询中其他地方的WINDOW子句定义的窗口规范的名称。有关详细信息,请参见 第 14.20.4 节,“命名窗口”。In the second case,window_nameis the name for a window specification defined by aWINDOWclause elsewhere in the query. For details, see Section 14.20.4, “Named Windows”.
对于 OVER
(window_spec) 语法,窗口规范有几个部分,都是可选的:For OVER
( syntax, the
window specification has several parts, all optional:window_spec)
window_spec: [window_name] [partition_clause] [order_clause] [frame_clause]
如果 OVER() 为空,窗口包括所有查询行,窗口函数使用所有行计算结果。否则,圆括号内的子句决定使用哪些查询行来计算函数结果,以及它们如何进行分区和排序:If OVER() is empty, the window consists of
all query rows and the window function computes a result using
all rows. Otherwise, the clauses present within the parentheses
determine which query rows are used to compute the function
result and how they are partitioned and ordered:
window_name:由查询中其他地方的WINDOW子句定义的窗口名称。如果window_name独立出现在OVER子句中,则完全定义该窗口。如果还提供了分区、排序或帧子句,则它们会修改对命名窗口的解释。详情请参阅 第 14.20.4 节,“命名窗口”。window_name: The name of a window defined by aWINDOWclause elsewhere in the query. Ifwindow_nameappears by itself within theOVERclause, it completely defines the window. If partitioning, ordering, or framing clauses are also given, they modify interpretation of the named window. For details, see Section 14.20.4, “Named Windows”.partition_clause:PARTITION BY子句指示如何将查询行划分为多个组。给定行的窗口函数结果基于包含该行的分区的行。如果省略PARTITION BY,则只有一个包含所有查询行的分区。partition_clause: APARTITION BYclause indicates how to divide the query rows into groups. The window function result for a given row is based on the rows of the partition that contains the row. IfPARTITION BYis omitted, there is a single partition consisting of all query rows.Note窗口函数的分区与表分区不同。有关表分区的信息,请参见 第 26 章,分区。Partitioning for window functions differs from table partitioning. For information about table partitioning, see Chapter 26, Partitioning.
partition_clause的语法如下:partition_clausehas this syntax:partition_clause: PARTITION BYexpr[,expr] ...标准 SQL 要求
PARTITION BY后面只能跟列名。MySQL 的一个扩展是允许表达式,而不仅仅是列名。例如,如果一张表包含名为ts的TIMESTAMP列,标准 SQL 允许PARTITION BY ts,但不允许PARTITION BY HOUR(ts),而 MySQL 两者都允许。Standard SQL requiresPARTITION BYto be followed by column names only. A MySQL extension is to permit expressions, not just column names. For example, if a table contains aTIMESTAMPcolumn namedts, standard SQL permitsPARTITION BY tsbut notPARTITION BY HOUR(ts), whereas MySQL permits both.order_clause:ORDER BY子句指示如何对每个分区中的行进行排序。根据ORDER BY子句相等的分区行被视为同行。如果省略ORDER BY,分区行是无序的,没有指定的处理顺序,并且所有分区行都是同行。order_clause: AnORDER BYclause indicates how to sort rows in each partition. Partition rows that are equal according to theORDER BYclause are considered peers. IfORDER BYis omitted, partition rows are unordered, with no processing order implied, and all partition rows are peers.order_clause的语法如下:order_clausehas this syntax:order_clause: ORDER BYexpr[ASC|DESC] [,expr[ASC|DESC]] ...每个
ORDER BY表达式后面可以可选地跟ASC或DESC来指示排序方向。如果未指定方向,默认是ASC。NULL值在升序排序时排在最前,在降序排序时排在最后。EachORDER BYexpression optionally can be followed byASCorDESCto indicate sort direction. The default isASCif no direction is specified.NULLvalues sort first for ascending sorts, last for descending sorts.窗口定义中的
ORDER BY仅在各个分区内应用。要对整个结果集进行排序,请在查询顶层包含ORDER BY。AnORDER BYin a window definition applies within individual partitions. To sort the result set as a whole, include anORDER BYat the query top level.frame_clause:帧是当前分区的一个子集,帧子句指定如何定义该子集。帧子句还有许多自己的子子句。详细信息请参见 第 14.20.3 节,“窗口函数帧规范”。frame_clause: A frame is a subset of the current partition and the frame clause specifies how to define the subset. The frame clause has many subclauses of its own. For details, see Section 14.20.3, “Window Function Frame Specification”.