mysql group by(手把手讲解)

更新时间:

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

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

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

基础用法与核心概念

MySQL 的 GROUP BY 是数据聚合操作的核心工具,它允许开发者将查询结果按照指定列的值进行分组,再对每组数据执行统计计算。例如,统计不同地区用户的订单数量,或计算某个时间段内商品的平均销售额。

形象地说,GROUP BY 的作用类似于整理文件夹:假设你有大量文件,按文件类型(如文档、图片、视频)分类存放,GROUP BY 就是按类型将文件分门别类,再对每个类别进行统计(如计算每类文件的数量)。

聚合函数与分组的配合

GROUP BY 必须与聚合函数(如 COUNT, SUM, AVG)结合使用,才能完成统计任务。例如:

SELECT department, COUNT(*) AS employee_count  
FROM employees  
GROUP BY department;  

此查询会将 employees 表按 department 列分组,统计每个部门的员工数量。

常用聚合函数

函数作用
COUNT()统计行数或非空值的数量
SUM()计算某一列的总和
AVG()计算某一列的平均值
MAX()/MIN()返回某一列的最大/最小值

分组字段的选择原则

分组字段的选择需符合以下逻辑:

  1. 唯一性:分组字段的值需能明确区分不同的组。例如,用 department_id 分组比用 department_name 更可靠(假设名称可能重复)。
  2. 业务相关性:分组字段应与统计目标直接相关。例如,统计销售额时,按 region 分组比按 user_id 更有意义。

进阶用法与常见场景

结合 WHEREHAVING 筛选数据

GROUP BY 的筛选需通过 HAVING 子句实现,而 WHERE 用于筛选原始数据。两者的区别可通过以下比喻理解:

  • WHERE:像“先筛选食材再分组”,例如先选出所有订单金额超过 1000 的记录,再按地区分组。
  • HAVING:像“分组后再筛选”,例如先按地区分组,再筛选出总销售额超过 10000 的地区。

示例代码

-- 先筛选订单金额大于 1000 的记录,再按地区分组统计  
SELECT region, SUM(amount) AS total_sales  
FROM orders  
WHERE amount > 1000  
GROUP BY region;  

-- 先按地区分组,再筛选总销售额超过 10000 的地区  
SELECT region, SUM(amount) AS total_sales  
FROM orders  
GROUP BY region  
HAVING SUM(amount) > 10000;  

多字段分组与嵌套查询

当需要更细粒度的统计时,可使用多个字段进行分组。例如,统计每个部门每月的平均工资:

SELECT department, MONTH(hire_date) AS month, AVG(salary) AS avg_salary  
FROM employees  
GROUP BY department, MONTH(hire_date);  

此查询的结果将展示不同部门在不同月份的平均薪资,便于分析薪资趋势。

子查询与 GROUP BY 的协作

在复杂场景中,GROUP BY 可与子查询结合使用。例如,统计每个用户的订单数量,并筛选出订单数超过 5 的用户:

SELECT user_id, COUNT(*) AS order_count  
FROM orders  
GROUP BY user_id  
HAVING order_count > 5;  

常见错误与解决方案

错误1:非分组字段未聚合

若查询包含未在 GROUP BY 中列出的非聚合字段,MySQL 默认会报错(除非配置了 ONLY_FULL_GROUP_BY 模式)。例如:

-- 错误示例:未聚合的字段 `product_name` 未出现在 GROUP BY 中  
SELECT product_name, SUM(quantity) AS total_sold  
FROM sales  
GROUP BY category;  

解决方案

  1. product_name 加入 GROUP BY(可能导致分组过细);
  2. 使用聚合函数(如 MAX(product_name));
  3. 确认是否需要调整分组逻辑。

错误2:HAVING 条件错误

HAVING 条件引用了别名(如 order_count),需确保该别名在 SELECT 中已定义。例如:

SELECT user_id, COUNT(*) AS order_count  
FROM orders  
GROUP BY user_id  
HAVING total_orders > 5;  -- 错误:应使用别名 `order_count`  

修正代码

HAVING order_count > 5;  

性能优化与最佳实践

索引的使用

在分组字段上建立索引可显著提升查询速度。例如,若频繁按 category 分组,可执行:

ALTER TABLE products ADD INDEX idx_category (category);  

避免在 GROUP BY 中使用复杂表达式

直接分组字段优于使用函数或计算表达式。例如,用 DATE(order_date) 分组时:

-- 低效写法  
GROUP BY DATE(order_date);  

-- 高效写法(假设已有 `order_date` 索引)  
GROUP BY order_date;  

使用 EXPLAIN 分析执行计划

通过 EXPLAIN 分析查询的执行效率。例如:

EXPLAIN SELECT region, SUM(amount)  
FROM orders  
GROUP BY region;  

此命令会展示 MySQL 如何执行查询,帮助定位性能瓶颈。


实战案例:电商销售数据分析

案例背景

假设有一个 sales 表,字段包括 product_id, category, price, quantity, sale_date。目标是分析各品类的销售情况。

案例代码

  1. 统计各品类的总销售额
SELECT category, SUM(price * quantity) AS total_revenue  
FROM sales  
GROUP BY category;  
  1. 按季度分组统计销售额
SELECT  
  CONCAT(YEAR(sale_date), '-', QUARTER(sale_date)) AS quarter,  
  SUM(price * quantity) AS revenue  
FROM sales  
GROUP BY quarter  
ORDER BY quarter;  
  1. 筛选出总销量超过 1000 的品类
SELECT category, SUM(quantity) AS total_sold  
FROM sales  
GROUP BY category  
HAVING total_sold > 1000;  

总结与扩展

MySQL GROUP BY 是数据分析的核心工具,通过合理使用聚合函数、分组字段和筛选条件,可高效提取数据价值。掌握其原理与最佳实践,能显著提升开发效率和数据洞察力。

对于进阶开发者,可进一步探索以下方向:

  • 窗口函数:在不改变表结构的情况下实现更复杂的分组计算;
  • 多表关联分组:结合 JOIN 操作进行跨表聚合分析;
  • 动态分组:使用变量或临时表实现灵活的分组逻辑。

通过持续实践与优化,GROUP BY 将成为你处理复杂数据场景的得力助手。

最新发布