SQL GROUP BY 语句(手把手讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在数据查询的世界里,SQL GROUP BY 语句如同一把钥匙,能够解锁海量数据中的隐藏模式。无论是统计销售数据、分析用户行为,还是优化数据库性能,开发者都需要掌握这一核心工具。本文将通过循序渐进的讲解,结合实际案例和代码示例,帮助读者全面理解 SQL GROUP BY 的原理与应用场景。


一、基础概念:什么是 GROUP BY?

1.1 分组的直观理解

GROUP BY 是 SQL 中用于将查询结果按指定列分组的语句。想象一个图书馆的书架:若想统计每个分类(如“小说”“科技”“历史”)的书籍数量,可以通过将书籍按分类标签分组,再对每组进行计数。GROUP BY 的作用正是如此——它将表中的行划分到不同的逻辑组中,随后可对每个组执行聚合计算(如求和、平均值、计数等)。

1.2 基本语法结构

SELECT 列1, 列2, 聚合函数(列3)  
FROM 表名  
WHERE 条件  
GROUP BY 列1, 列2  
ORDER BY 列;  

关键点:

  • GROUP BY 必须与聚合函数(如 SUM(), COUNT(), AVG())配合使用,否则无法直接返回非分组列的原始数据。
  • 分组字段需出现在 SELECT 子句中,除非使用聚合函数。

二、核心用法:从简单到复杂

2.1 基本分组:单列分组

案例场景:某电商数据库有 sales 表,包含 product_id, category, price, sale_date 等字段。需求是统计每个商品类别的总销售额。

SELECT category, SUM(price) AS total_sales  
FROM sales  
GROUP BY category;  

输出结果示例:
| category | total_sales |
|------------|-------------|
| 电子产品 | 150000 |
| 家居用品 | 80000 |
| 图书 | 30000 |

2.2 多列分组:细分数据维度

若需按多个字段分组(例如按“类别”和“销售月份”统计),只需在 GROUP BY 中添加字段:

SELECT category,  
       DATE_TRUNC('month', sale_date) AS sale_month,  
       SUM(price) AS total_sales  
FROM sales  
GROUP BY category, DATE_TRUNC('month', sale_date)  
ORDER BY sale_month DESC;  

此查询将生成按月分组的销售数据,帮助分析不同类别的季节性趋势。


三、聚合函数:与 GROUP BY 的协同作用

3.1 常见聚合函数详解

  • COUNT():统计组内行数。
  • SUM():计算数值列的总和。
  • AVG():计算数值列的平均值。
  • MAX() / MIN():获取列中的最大值或最小值。

案例:统计每个用户的订单数量及平均订单金额:

SELECT user_id,  
       COUNT(order_id) AS order_count,  
       AVG(amount) AS avg_order_amount  
FROM orders  
GROUP BY user_id;  

3.2 聚合函数的隐式分组

即使未显式使用 GROUP BY,某些聚合函数(如 SUM(*))会默认对全表进行分组。但若需按条件分组,必须显式声明。


四、进阶技巧:HAVING 子句与子句顺序

4.1 HAVING:筛选分组后的结果

HAVINGGROUP BY 的“过滤器”,用于筛选满足条件的组。例如,仅显示总销售额超过 10 万元的类别:

SELECT category, SUM(price) AS total_sales  
FROM sales  
GROUP BY category  
HAVING SUM(price) > 100000;  

注意HAVING 需作用于聚合结果,而 WHERE 用于过滤原始数据行。

4.2 子句的正确顺序

SQL 语句的执行顺序为:

FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY  

这意味着 GROUP BY 必须在 HAVING 之前,而聚合函数需在 SELECT 中定义。


五、高级场景:窗口函数与多级分组

5.1 窗口函数与 GROUP BY 的对比

GROUP BY 是“行聚合”,而窗口函数(如 OVER())可在保留原始行的基础上进行分组计算。例如,计算每个用户订单金额的排名:

SELECT user_id,  
       amount,  
       RANK() OVER (PARTITION BY user_id ORDER BY amount DESC) AS order_rank  
FROM orders;  

此查询保留所有订单行,但按用户分组并排序。

5.2 多级分组与 ROLLUP/CUBE

ROLLUPCUBE 可自动生成嵌套分组。例如:

SELECT category,  
       DATE_TRUNC('month', sale_date) AS sale_month,  
       SUM(price) AS total_sales  
FROM sales  
GROUP BY ROLLUP(category, sale_month);  

此查询会返回“按类别和月份分组”“仅按类别分组”“总销售额”三组结果,适合生成多层级报表。


六、常见陷阱与解决方案

6.1 非分组列的错误引用

错误示例:

SELECT category, price, SUM(price)  
FROM sales  
GROUP BY category; -- 错误!price 未聚合或分组  

解决方法

  • price 放入聚合函数(如 AVG(price))或添加到 GROUP BY 子句中。

6.2 分组字段顺序的影响

多字段分组时,顺序决定分组优先级。例如:

GROUP BY category, sale_month; -- 先按 category 分组,再按 sale_month  

若顺序调换,分组逻辑将完全不同。


七、实战案例:电商销售分析

7.1 案例背景

假设某电商平台有以下表结构:

  • users(用户表):user_id, registration_date, country
  • orders(订单表):order_id, user_id, amount, order_date

7.2 案例问题与解决方案

问题 1:统计各国用户的订单数量及平均订单金额。

SELECT u.country,  
       COUNT(DISTINCT o.order_id) AS total_orders,  
       AVG(o.amount) AS avg_order_amount  
FROM users u  
JOIN orders o ON u.user_id = o.user_id  
GROUP BY u.country;  

问题 2:找出订单金额前 10% 的用户。

SELECT user_id,  
       SUM(amount) AS total_spent  
FROM orders  
GROUP BY user_id  
HAVING SUM(amount) >= (  
    SELECT PERCENTILE_CONT(0.9)  
    WITHIN GROUP (ORDER BY total)  
    FROM (  
        SELECT SUM(amount) AS total  
        FROM orders  
        GROUP BY user_id  
    ) AS subquery  
);  

结论

SQL GROUP BY 语句是数据分析师和开发者的必备工具,它通过分组与聚合,将原始数据转化为可洞察的信息。无论是基础的统计需求,还是复杂的多级分组,掌握其原理与技巧都能显著提升数据处理效率。建议读者通过实际项目练习,结合 HAVING、窗口函数等进阶功能,逐步解锁 GROUP BY 的全部潜力。

通过本文的学习,读者应能理解:

  1. 如何用 GROUP BY 实现数据分组与聚合计算;
  2. 区分 WHEREHAVING 的使用场景;
  3. 通过案例掌握多列分组、嵌套查询等复杂操作。

掌握这些技能后,开发者将能更高效地从数据中提取价值,为业务决策提供坚实的技术支持。

最新发布