导读:本期聚焦于广州网站建设创作的《SQL如何在不改变行数前提下进行分组统计?使用窗口函数实现》,敬请观看详情。在SQL数据处理场景中,经常需要既得到分组维度的统计结果,又保留原始数据的每一行记录,传统的GROUP BY语句会改变行数无法满足需求。窗口函数可以在不改变原表行数的情况下,按指定分组规则计算统计值,同时保留所有原始字段。本文将介绍窗口函数的基本语法,结合SUM、COUNT、AVG等常用聚合函数,演示如何在不改变行数的前提下完成分组统计,同时对比窗口函数与传统GROUP BY的差异,帮助开发者快速掌握该场景下的实现方法。

在SQL数据处理工作中,分组统计是一种高频需求。无论是按地区汇总销售额、按部门计算平均绩效,还是按用户统计订单数量,开发者通常都会想到GROUP BY。然而传统的GROUP BY有一个明显的限制:它会把同一分组的多行数据压缩为一行,查询结果的行数等于分组数量,原始明细行会丢失。如果业务场景要求同时保留每一行明细数据以及该行所属分组的汇总值,例如在销售明细报表中既要看到每笔订单的金额,也要看到该地区的总销售额,就需要使用窗口函数。窗口函数可以在不合并行的情况下,为每一行计算分组级别的统计值,从而保留完整的原始数据集。

窗口函数并不是新的聚合函数,而是对聚合函数或其他分析函数的一种计算方式扩展。通过OVER子句,窗口函数能够定义数据行的分组范围、排序规则和计算窗口,使得每一行都可以携带分组统计信息。这种方式在BI报表、数据核对、排名分析等场景中非常实用。

窗口函数的基本语法与分组机制

窗口函数的核心语法由函数名和OVER子句两部分组成。函数名可以是SUM、COUNT、AVG等常规聚合函数,也可以是ROW_NUMBER、RANK等专用分析函数。OVER子句用于定义窗口的计算范围,基本格式如下:

函数名(字段) OVER (
    PARTITION BY 分组字段
    ORDER BY 排序字段
    窗口帧定义
) AS 别名

其中,PARTITION BY负责指定分组维度,它的作用与GROUP BY类似,但不会合并数据行。ORDER BY用于定义分组内的排序规则,只有涉及累计、排名等顺序相关的计算时才需要使用。窗口帧定义用来约束每一行参与计算的行集合,默认情况下,如果不指定窗口帧,聚合函数会计算整个分区的所有行。

需要特别说明的是,窗口函数与普通聚合函数在求值方式上存在本质区别。普通聚合函数在GROUP BY查询中只返回每个分组一行结果,而窗口函数会在每一行上独立执行,但计算范围由OVER子句中的分区和帧参数控制。因此,即使同一分区的不同行,窗口函数的结果也可能不同,这取决于窗口帧是否随当前行移动。例如,当使用ORDER BYROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW时,窗口帧就会从分区起始行一直延伸到当前行,从而实现累计计算。

聚合函数结合窗口函数的分组统计示例

为了更直观地说明窗口函数如何在不改变行数的前提下完成分组统计,下面以一张销售记录表sales为例。该表包含订单编号、地区、销售人员和销售金额四个字段,原始数据如下:

idregionsalespersonamount
1华东张三1500
2华东李四2000
3华北王五1800
4华东赵六1200
5华北钱七2200

创建该表并插入示例数据的SQL语句如下:

CREATE TABLE sales (
    id INT PRIMARY KEY,
    region VARCHAR(20),
    salesperson VARCHAR(20),
    amount DECIMAL(10,2)
);

INSERT INTO sales (id, region, salesperson, amount) VALUES
(1, '华东', '张三', 1500),
(2, '华东', '李四', 2000),
(3, '华北', '王五', 1800),
(4, '华东', '赵六', 1200),
(5, '华北', '钱七', 2200);

现在假设需要生成一张报表:保留全部5条销售明细记录,同时在每一行后面增加一个字段,显示该行所属地区的销售总额。如果使用传统的SUM配合GROUP BY,查询结果会被压缩为每个地区一行,无法同时展示明细。此时窗口函数就能派上用场:

SELECT
    id,
    region,
    salesperson,
    amount,
    SUM(amount) OVER (PARTITION BY region) AS region_total_amount
FROM sales;

执行结果如下:

idregionsalespersonamountregion_total_amount
1华东张三15004700
2华东李四20004700
3华北王五18004000
4华东赵六12004700
5华北钱七22004000

从结果中可以看到,原始5行数据全部保留,同时每一行都新增了region_total_amount列。华东地区的三行记录都显示总额4700,华北地区的两行记录都显示总额4000。这种写法在数据明细报表中非常常见,因为它既能满足审计或核对需求,又能快速查看分组汇总值。

除了SUM之外,COUNT和AVG同样可以使用窗口函数实现分组统计。例如,需要在每一行展示所在地区的销售人员数量和平均销售金额,可以这样写:

SELECT
    id,
    region,
    salesperson,
    amount,
    COUNT(*) OVER (PARTITION BY region) AS region_person_count,
    AVG(amount) OVER (PARTITION BY region) AS region_avg_amount
FROM sales;

该查询会在每一行上计算华东或华北分区的记录数以及平均金额。窗口函数并不会改变原始表的行顺序和行数,只是在结果集中增加了计算列。如果还需要同时查看每个地区的最大值、最小值,也可以将MAX(amount)MIN(amount)与OVER子句组合,用法完全一致。

窗口函数与GROUP BY的差异及使用建议

窗口函数与GROUP BY都可以完成分组统计,但二者在结果形态和适用场景上有明显区别。GROUP BY的目的是把数据按指定列进行聚合,结果集的行数等于分组数,原始明细行会消失。如果想让GROUP BY查询同时返回明细字段,必须把所有明细字段都加入GROUP BY子句,或者使用子查询先聚合再关联,但这样会增加SQL复杂度和执行成本。窗口函数则直接作用于每一行,不会改变源表的行数,所有明细字段天然保留,同时还能附加分组统计值。

  • GROUP BY:合并分组行,结果行数减少,适合只需要分组汇总值的聚合报表。
  • 窗口函数:保留所有原始行,适合明细与汇总值同时展示的报表、排名、累计等分析场景。

在使用窗口函数时,有几点需要特别注意。PARTITION BY在OVER子句中是可选的。如果省略,窗口函数会把整张表视为一个分区,从而计算全局统计值,例如全表总销售额。此时查询结果中每一行都会出现同一个全局汇总值。如果需要按多个字段分组,也可以在PARTITION BY后面列出多个字段,用逗号分隔,和GROUP BY多字段分组的逻辑类似。

另一个容易忽略的细节是窗口帧的定义。对于聚合窗口函数,如果不指定ORDER BY和窗口帧,默认窗口帧为RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,也就是整个分区的所有行。这意味着无论当前行是哪一行,计算范围都包含分区内的全部记录。但如果在OVER子句中添加ORDER BY,并且不显式指定窗口帧,某些数据库的默认行为会改为从分区起始行到当前行,具体行为可能因数据库产品而异。因此,在需要明确的累计或移动统计时,建议显式写出窗口帧,例如:

-- 按地区分组,按销售金额升序计算累计销售额
SELECT
    id,
    region,
    salesperson,
    amount,
    SUM(amount) OVER (
        PARTITION BY region
        ORDER BY amount
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS region_cumulative_amount
FROM sales;

这个查询会先按region分区,再按amount升序排列,每行的累计值从分区内金额最小的行一直累加到当前行。通过调整窗口帧,还可以实现移动平均、滚动求和等更灵活的分析功能。

综上所述,窗口函数是SQL中非常强大的分析工具,能够在保留原始数据行数的前提下完成分组统计。掌握OVER子句、PARTITION BY以及窗口帧的配合使用,可以显著提升复杂报表的编写效率和可读性。在实际工作中,如果需要同时查看明细数据与分组汇总值,应当优先考虑使用窗口函数替代传统的GROUP BY加子查询关联方案。

SQL窗口函数分组统计over_clause修改时间:2026-07-20 07:09:11

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。