MySQL 窗口函数

2024年10月21日 MySQL 窗口函数 极客笔记

MySQL 窗口函数

MySQL中的窗口函数用于在与 当前行 相关的一组行上进行计算。当前行是进行函数计算的那行。窗口函数执行类似于使用聚合函数进行的计算。但是,与在整个表上执行操作的聚合函数不同,窗口函数不产生要分组到一行中的结果。这意味着窗口函数在一组行上执行操作,并为每行 生成一个聚合值 。因此,每行保持其唯一身份。

窗口函数是在MySQL版本8中引入的新功能,可提高查询的执行性能。这些函数允许我们更有效地解决与查询相关的问题。

语法

使用窗口函数的基本语法如下:

window_function_name(expression) 
OVER (
    [partition_defintion]
    [order_definition]
    [frame_definition]
)

在语法中,可以看到我们首先指定了窗口函数的名称,然后是一个表达式。然后,我们指定了包含三个表达式的 OVER 子句,它们是 partition_definition, order_definition, and frame_definition .

它确保一个OVER子句始终有开括号和闭括号,即使它没有任何表达式。

让我们看一下OVER子句中使用的每个表达式的语法:

PARTITION BY子句

该子句用于将行分割或分组成分区,并且分区边界将这些分区分隔开。窗口函数对每个分区进行操作,当它穿过分区边界时,会再次进行初始化。该子句的语法如下:

PARTITION BY <expression>[{,<expression>...}]

在分区子句中,我们可以定义一个或多个由逗号分隔的表达式。

ORDER BY子句

该子句用于指定分区内行的顺序。以下是ORDER BY子句的语法:

ORDER BY <expression> [ASC|DESC], [{,<expression>...}]

我们还可以使用它来根据多个键在分区内对行进行排序,每个键由一个表达式指定。该子句还可以定义一个或多个由逗号分隔的表达式。尽管ORDER BY子句可以与所有窗口函数一起使用,但建议将其与顺序敏感的窗口函数一起使用。

帧子句

帧是窗口函数中当前分区的子集。因此,我们使用帧子句来定义当前分区的子集。使用帧子句创建当前分区的子集的语法如下:

frame_unit {<frame_start>|<frame_between>}

我们可以使用当前行来定义一个框架,该框架可以根据当前行的位置在分区内进行移动。

在语法中,frame_unit可以是ROWS或RANGE,用于定义框架行与当前行之间的关系类型。如果frame_unit是ROWS,则框架行和当前行的偏移量是行号。而如果frame_unit是RANGE,则偏移量是行值。

frame_start和frame_between表达式用于指定框架边界。frame_start表达式有三个条件:

UNBOUNDED PRECEDING:这里,框架从当前分区的第一行开始。

N PRECEDING:这里,N是一个字面数值或一个求值为数值的表达式。它表示第一个当前行之前的行数。

CURRENT ROW:它指定了最近计算的行。

frame_between表达式可以写成:

BETWEEN frame_boundary_1 AND frame_boundary_2

上述表达可以有以下一些内容:

frame_start: 我们之前已经解释过了。

UNBOUNDED FOLLOWING :它指定在分区中最后一行的帧结束。

N FOLLOWING: 它是当前行后面的物理N行。

如果在OVER子句中未指定frame_definition,则默认情况下, MySQL 使用以下帧:

RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

窗口函数概念

在本节中,我们将看到如何在MySQL中使用窗口函数。因此,让我们首先使用以下语句创建名为 “Sales” 的表:

CREATE TABLE Sales(
    Employee_Name VARCHAR(45) NOT NULL,
    Year INT NOT NULL,
            Country VARCHAR(45) NOT NULL,
    Product VARCHAR(45) NOT NULL,
    Sale DECIMAL(12,2) NOT NULL,
    PRIMARY KEY(Employee_Name, Year)
);

接下来,我们需要使用INSERT语句将记录添加到表中,如下所示:

INSERT INTO Sales(Employee_Name, Year, Country, Product, Sale)
VALUES('Joseph', 2017, 'India', 'Laptop', 10000),
('Joseph', 2018, 'India', 'Laptop', 15000),
('Joseph', 2019, 'India', 'TV', 20000),
('Bob', 2017, 'US', 'Computer', 15000),
('Bob', 2018, 'US', 'Computer', 10000),
('Bob', 2019, 'US', 'TV', 20000),
('Peter', 2017, 'Canada', 'Mobile', 20000),
('Peter', 2018, 'Canada', 'Calculator', 1500),
('Peter', 2019, 'Canada', 'Mobile', 25000);

要验证数据记录是否存在于表中,请使用SELECT语句:

mysql> SELECT * FROM Sales;

执行后,我们可以看到记录已成功添加到表中。

为了理解窗口函数,让我们先看一下在MySQL中聚合函数是如何工作的。聚合函数评估多行并将结果集合并为一行。所以,执行下面的语句使用了聚合函数”SUM”,并返回给定年份中所有员工的销售总额:

mysql> SELECT SUM(sale) AS Total_Sales FROM Sales;

输出

再次,我们使用带有 “SUM” 函数和 GROUP BY 子句的方法,该方法适用于子行集。因此,执行以下语句,返回按特定年份分组的所有产品的总销售额:

mysql> SELECT Year, Product, SUM(Sale) AS Total_Sales 
FROM Sales 
GROUP BY Year 
ORDER BY Product;

输出

在这两个示例中,我们可以看到聚合函数在查询执行后将行数减少到一行。

与聚合函数类似,窗口函数也是使用行的一个子集,但它不会将结果集减少到一行。这意味着窗口函数对一组行进行操作,并为每行生成一个聚合值。 例如, 执行以下语句,返回每个产品的销售额以及给定年份的产品总销售额:

mysql> SELECT Year, Product, Sale, SUM(Sale) 
OVER(PARTITION BY Year) AS Total_Sales 
FROM Sales;

输出

在上面的示例中,我们可以看到窗口操作使用了 OVER 子句,该子句负责将查询行分组成窗口函数处理的组。在这里,OVER子句按年份对行进行分区,并在每个分区上产生一个总和。成功计算后,它将这个总和对应到每个分区的行。

窗口函数的类型

我们主要将窗口函数分为三种类型,如下所示:

聚合函数

它是一个在多行上操作并将结果产生在单行上的函数。一些重要的聚合函数有:

COUNT、SUM、AVG、MIN、MAX等等。

排名函数

它是一种允许我们对给定表中的每个分区的每一行进行排名的函数。一些重要的排名函数有:

RANK、DENSE_RANK、PERCENT_RANK、ROW_NUMBER、CUME_DIST等等。

分析函数

它是一种由幂级数局部表示的函数。一些重要的分析函数有:

NTILE、LEAD、LAG、NTH、FIRST_VALUE、LAST_VALUE等等。

分析函数示例

在这里,我们将使用 NTILE 窗口函数。该函数接受一个整数值作为参数,将该组划分为多个整数值。例如,如果我们使用 NTILE(4) ,则将总记录数分为四个组。当总记录是奇数时,它将奇数记录添加到第一行。下面的查询更清楚地解释了这一点。

SELECT Year, Product, Sale, 
NTile(4) OVER() AS Total_Sales 
FROM Sales;

输出

在上面的输出中,我们可以看到总共有9行。因此,NTILE函数将其分为四行,并将其中一个额外行添加到第一行中。

让我们看另一个使用 “LEAD” 函数的例子。这个函数用于查询一张表中的多行而不需要将表本身连接起来。它意味着我们可以从当前行访问 下一行 的数据。它返回下一行的输出。执行以下语句以更清楚地了解:

SELECT Year, Product, Sale, 
LEAD(Sale,1) OVER(ORDER BY Year) AS Total_Sales 
FROM Sales;

输出

本文链接:http://so.lmcjl.com/news/15917/

展开阅读全文