SQL 窗口函数2025 年 8 月 18 日 | 14 分钟阅读 结构化查询语言 (SQL) 不仅仅是选择、插入或更新表中的数据。窗口函数,也称为分析函数,对于执行分析任务非常有用,例如排名、计算移动平均值(不分组或丢失行级数据)或运行总计。 ![]() SQL 中的窗口函数是什么?窗口函数对与当前行(称为窗口帧)相关的表行集执行计算。窗口函数为每一行返回一个值并提供额外的详细信息,这与聚合函数(如 SUM() 和 AVG())不同,聚合函数为每个组返回一个值。 示例: 聚合函数就像计算城市的平均温度,而窗口函数就像计算每天的温度以及城市的平均温度。 ![]() 语法 参数
SQL 窗口函数的类型下面讨论了各种类型的 SQL 窗口函数 1. 排名窗口函数这些函数用于根据指定标准为分区内的每一行分配排名。 以下是一些窗口排名函数
2. 聚合窗口函数这些函数用于对定义的行集(窗口帧)执行计算。它与常规聚合函数不同,因为聚合窗口函数在结果中返回单独的行,而常规聚合函数返回单个聚合结果。 以下是一些聚合窗口函数
3. 值窗口函数这些函数用于从相对于当前行的不同行返回一个值。 以下是一些值窗口函数
SQL 窗口函数示例让我们考虑一个简单的 Employees 表来解释窗口函数的所有概念 首先,让我们创建一个名为 Employees 的表并向其中插入数据。 创建表格 插入数据 获取表 输出
排名窗口函数示例 1:使用 RANK() 窗口函数RANK() 窗口函数为分区内的每一行分配唯一的排名。如果两行具有相同的值,则两行将获得相同的排名,并且会跳过下一个排名,这意味着排名中会出现一个间隙。 查询 输出
说明 在上述查询中,有一个排名窗口函数,它根据 ORDER BY 子句和降序为每个分区内的每一行提供不同的排名,这意味着每个部门的排名是单独进行的。我们使用了 RANK(),因此在输出中,该函数为每一行提供了唯一的排名,但在 EmployeeID(2 和 7)之间存在并列,因此我们可以看到该函数为两行提供了相同的排名,但跳过了下一个排名并跳到 4 处理下一个值。 示例 2:使用 DENSE_RANK() 窗口函数DENSE_RANK() 窗口函数类似于 RANK(),但如果行中存在重复值,它不会跳过下一个排名,这意味着排名中不会有间隙。这种间隙是 RANK() 与 DENSE_RANK() 的区别所在。 查询 输出
说明 在上述查询中,有一个排名窗口函数,它根据 ORDER BY 子句和降序为每个分区内的每一行提供不同的排名,这意味着每个部门的排名是单独进行的。我们使用了 DENSE_RANK(),因此在输出中,该函数为每一行提供了唯一的排名,但在 EmployeeID(2 和 7)之间存在并列,因此我们可以看到该函数为两行提供了相同的排名,但下一个排名没有跳过。 示例 3:使用 ROW_NUMBER() 窗口函数ROW_NUMBER() 窗口函数为分区内的每一行分配唯一的序列号,即使两行中的值相同。 查询 输出
说明 在上述查询中,有一个排名窗口函数,它根据 ORDER BY 子句和降序为每个分区内的每一行提供不同的序列号,这意味着每个部门都获得一个不同的序列号。我们使用了 ROW_NUMBER(),因此在输出中,该函数为每一行提供了唯一的序列号,但在 EmployeeID(2 和 7)之间存在并列,但我们可以看到该函数仍然为两行提供了唯一的排名。 示例 4:使用 NTILE(n) 窗口函数NTILE(n) 窗口函数将结果集划分为 n 个桶(即每个分区内的相等部分),并为每一行分配一个桶号。 查询 输出
说明 在上述查询中,有一个 NTILE(2) 排名窗口函数,它根据薪资降序将每个部门分成两个组。在输出中,我们可以看到每个部门都分成两个组。 注意:如果行无法均匀拆分,例如 HR 的 3 行分成 2 组,那么 NTILE() 会将额外的行分配到前面的组中,因此组 1 可能比组 2 拥有更多的行。聚合窗口函数示例 1:使用 AVG() 窗口函数AVG() 窗口函数计算窗口中的平均值。特定部门的每一行的平均值将相同。 查询 输出
说明 在上述查询中,有一个聚合窗口函数,它计算特定部门每一行员工的平均工资。在输出中,我们可以看到 AverageSalary 列中 HR 和 IT 部门每一行的平均工资相同。 示例 2:使用 SUM() 窗口函数SUM() 窗口函数用于查找每个分区的总值,并在每一行旁边显示总计。 查询 输出
说明 在上述查询中,有一个聚合窗口函数,它计算每个部门的总工资。在输出中,我们可以看到 TotalSalary 列中每个部门的总工资。 示例 3:使用 MIN() 窗口函数MIN() 窗口函数用于查找每个分区的最小值,并在结果集的每一行旁边显示该最小值。 查询 输出
说明 在上述查询中,有一个聚合窗口函数,它计算每个部门的最低工资。在输出中,我们可以看到 MinSalary 列中每个部门的最低工资。 示例 4:使用 MAX() 窗口函数MAX() 窗口函数用于查找每个分区的最大值,并在结果集的每一行旁边显示该最大值。 查询 输出
说明 在上述查询中,有一个聚合窗口函数,它计算每个部门的最高工资。在输出中,我们可以看到 MaxSalary 列中每个部门的最高工资。 示例 5:使用 COUNT() 窗口函数COUNT() 窗口函数用于计算窗口中的行数,并在结果集的每一行旁边显示此计数。 查询 输出
说明 在上述查询中,有一个聚合窗口函数,它显示每个部门的员工人数。在输出中,我们可以看到 EmployeeCount 列中每个部门的员工人数。 值窗口函数示例 1:使用 LEAD() 窗口函数LEAD() 窗口函数用于查找分区内下一行的值,并在结果集的每一行旁边显示该值。 查询 输出
说明 在上述查询中,有一个聚合窗口函数,它返回下一个工资。在输出中,我们可以在每一行旁边观察到下一个工资。 示例 2:使用 LAG() 窗口函数LAG() 窗口函数用于查找分区内上一行的值,并在结果集的每一行旁边显示该值。 查询 输出
说明 在上述查询中,有一个聚合窗口函数,它返回上一个工资。在输出中,我们可以在每一行旁边观察到上一个工资。 示例 3:使用 FIRST_VALUE() 窗口函数FIRST_VALUE() 窗口函数返回分区内的第一个值,并在结果集的每一行旁边显示该值。 查询 输出
说明 在上述查询中,有一个聚合窗口函数,它返回上一个工资。在输出中,我们可以在每一行旁边观察到上一个工资。 示例 4:使用 LAST_VALUE() 窗口函数LAST_VALUE() 返回窗口帧中的最后一个值。LAST_VALUE() 只考虑从第一行到当前行的范围。我们可能需要显式定义帧并在每一行旁边显示值。 查询 输出
说明 在上述查询中,有一个聚合窗口函数,它给出下一个工资。我们可能需要显式指定使用 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 的帧,以便帧从分区的第一行跨越到最后一行,并且 LAST_VALUE() 函数返回最后一个值。我们可以在结果集的每一行中观察到下一个工资。 SQL 窗口函数的优点![]() 保留行级详细信息: 它允许执行聚合,而不会像 GROUP BY 那样折叠原始行。它在结果中显示聚合值和非聚合值。 可重用表达式: 它允许在同一个 SELECT 语句中应用多个窗口函数。 启用复杂分析: 它轻松计算复杂计算,例如运行总计、移动平均值、排名等。这些复杂计算可以在单个查询中完成,否则它们需要多个步骤、子查询等。 高度灵活: 它与 PARTITION BY、ORDER BY 和帧子句一起使用,以控制每个计算的数据。 高效报告: 它简化了生成仪表板、报告和分页报告,而无需使用多个联接或子查询。 提高了可读性: 它简化了逻辑,减少了在许多情况下使用子查询和复杂联接的需要。 SQL 窗口函数的缺点![]() 性能开销: 如果未正确索引或优化,它在大型数据集上可能会更慢。 复杂性和可读性: 窗口函数的语法很复杂,因为查询会变得很长且难以维护。理解复杂的查询变得困难,这种复杂性会影响代码可读性。 内存使用增加: 与常规 SQL 语句相比,SQL 窗口函数占用更多内存,因此在处理大型数据集时会消耗更多空间。 资源密集型操作: SQL 窗口函数需要额外的处理,这会导致对大量计算资源的需求,尤其是在大型数据集上。 调试挑战: 与简单的 SQL 语句相比,由于多个窗口函数,调试复杂查询很困难。 仅限于特定 RDBMS: 较旧的数据库版本可能不支持或功能有限,这意味着并非所有数据库都可以使用 SQL 窗口函数。 结论SQL 窗口函数对于高级数据分析非常重要,它在处理分区数据时提供了灵活性。我们可以通过使用 OVER、PARTITION BY 和 ORDER BY 子句来执行复杂的计算,例如聚合计算、累积总计和排名,同时保留行级数据。我们已经理解了各种类型的窗口函数,它们为跨不同类型数据集的数据操作提供了灵活的方法。 常见问题解答 (FAQs)1. RANK() 和 ROW_NUMBER() 有什么区别?
以下是 RANK() 和 ROW_NUMBER() 之间的区别 2. 我应该何时使用 PARTITION BY 子句? 您可以使用 PARTITION BY 子句在执行窗口操作之前根据指定列将行分成组。它允许使用窗口函数(例如 RANK()、SUM() 等)对每个组执行计算。 3. 我可以在没有 PARTITION BY 的情况下使用窗口函数吗? 是的,您可以在没有 PARTITION BY 子句的情况下使用窗口函数。窗口函数将整个结果集视为单个分区进行操作。 4. 聚合函数和窗口函数有什么区别?
5. 所有数据库都支持窗口函数吗? 不,并非所有数据库都支持窗口函数。一些较旧的或专门的数据库系统可能不支持窗口函数,但大多数现代关系数据库都支持窗口函数,例如 PostgreSQL、SQL Server、MySQL (v8+)、Oracle 和 SQLite。 6. 我可以在一个查询中使用多个窗口函数吗? 当然。您可以在一个查询中使用多个窗口函数。每个窗口函数都包含自己的 OVER 子句,允许为每个函数进行不同的分区和排序。 示例: 一个查询可以使用一个窗口函数计算运行总和,并使用另一个窗口函数计算移动平均值。所有这些函数都可以在同一个 SELECT 语句中通过单个查询完成。 下一个主题Coalesce SQL |
我们请求您订阅我们的新闻通讯以获取最新更新。