乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 数据库基础与安装

    • MySQL 简介
    • MySQL 5.6 到 5.7 到 8.0 关键差异速查
    • 安装 MySQL 5.7
    • 连接与断开服务器
    • 创建数据库
    • 创建数据表
    • 数据库与数据表
    • 加载数据
    • 获取数据库信息
    • 批处理模式
    • SHOW 语句汇总
    • FLUSH 与 RESET 语句
    • my.cnf 核心参数
    • 字符集与排序规则
  • 第2章 SQL基础查询

    • SELECT
    • WHERE
    • ORDER BY
    • LIMIT
    • COUNT
    • 聚合函数
    • 比较运算符
    • 逻辑运算符
    • 算术运算符
    • 模式匹配
    • NULL 值处理
    • UPDATE
    • DELETE
    • REPLACE
    • SELECT INTO
  • 第3章 数据类型与运算符

    • 数值类型
    • 字符串类型
    • 日期时间类型
    • BIT 类型
    • ENUM 类型
    • SET 类型
    • JSON 类型
    • 类型转换
  • 第4章 函数与表达式

    • 字符串函数
    • 数值函数
    • 日期函数
    • 全文检索函数
  • 第5章 高级查询与子查询

    • JOIN
    • 子查询
    • UNION
    • GROUP BY
    • HAVING
    • DISTINCT
  • 第6章 表与索引

    • 数据定义语言
    • 修改表结构
    • 视图
    • 修改视图与检查选项
    • 外键
    • 索引
    • 唯一索引
    • 复合索引
    • 存储引擎对比
    • 分区表
    • 第一范式与第二范式
    • 第三范式与 BC 范式
    • 反范式设计
  • 第7章 存储过程与函数

    • 存储过程
    • 存储函数
    • 变量
    • 流程控制
    • 游标
    • 预处理语句
  • 第8章 事务与锁

    • 事务
    • 事务隔离级别
    • 锁机制
    • MVCC
    • 死锁专题分析
    • LOCK TABLES
    • XA 事务
  • 第9章 用户管理与安全

    • 用户管理
    • 权限管理
    • 角色
    • SQL 注入防范
  • 第10章 性能优化入门

    • 执行计划
    • 索引优化
    • 查询优化
    • 查询优化器提示
    • 慢查询日志
    • InnoDB 深入机制
    • InnoDB 专项优化
    • Performance Schema
    • sys Schema
  • 第11章 复制与高可用

    • 主从复制原理
    • 半同步复制配置
    • binlog 开启与 point-in-time 恢复
    • mysqldump 全库备份
    • mysqldump 单表与条件备份
    • mysqldump 恢复与导入
    • xtrabackup 全量热备
    • xtrabackup 准备与恢复
    • xtrabackup 增量与流式备份
  • 第12章 触发器与事件

    • 触发器
    • 事件调度器
  • 参考

    • MySQL 5.7 专业术语大全
    • MySQL 5.7 关键字与保留字大全

聚合函数

导学

聚合函数是 SQL 的"统计利器"——它们接收多行输入,输出单一汇总值。求总人数、平均分、最高价格……这些需求都依赖聚合函数。

定义

聚合函数(Aggregate Functions):对一组值执行计算并返回单一值的函数。MySQL 5.7 核心聚合函数包括 COUNT、SUM、AVG、MAX、MIN、GROUP_CONCAT。

核心聚合函数

函数作用NULL 处理
COUNT(*)统计行数不忽略,统计所有行
COUNT(列)统计该列非 NULL 的行数忽略 NULL
SUM(列)求和忽略 NULL
AVG(列)平均值忽略 NULL
MAX(列)最大值忽略 NULL
MIN(列)最小值忽略 NULL
GROUP_CONCAT(列)将多行值拼接为字符串忽略 NULL

演示数据准备

DROP TABLE IF EXISTS employees;

CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    emp_name VARCHAR(20),
    dept VARCHAR(20),
    score DECIMAL(5,2)
);

INSERT INTO employees (emp_name, dept, score) VALUES
('大翔', '技术部', 100),
('白歌', '技术部', NULL);

当前 employees 表中的完整数据如下:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

SQL 示例

场景一:综合统计(COUNT / SUM / AVG / MAX / MIN)

当前数据状态:见上文 employees 表完整数据。

执行语句:

SELECT
    COUNT(*)          AS 员工总数,
    COUNT(score)      AS 有绩效分人数,
    COUNT(DISTINCT dept) AS 部门种类数,
    AVG(score)        AS 平均分,
    MAX(score)        AS 最高分,
    MIN(score)        AS 最低分,
    SUM(score)        AS 总分
FROM employees;

操作后结果:

员工总数有绩效分人数部门种类数平均分最高分最低分总分
211100.0000100.00100.00100.00

结果解读:

  • COUNT(*) = 2:统计所有行
  • COUNT(score) = 1:只统计 score 非 NULL 的行(白歌为 NULL)
  • COUNT(DISTINCT dept) = 1:去重后计数(技术部)
  • AVG(score) = 100.0000:100/1 = 100.0000,忽略 NULL(白歌)
  • MAX(score) = 100.00:最高分是大翔
  • MIN(score) = 100.00:最低分是大翔(唯一非 NULL 值)
  • SUM(score) = 100.00:所有非 NULL 绩效分之和

场景二:字符串拼接(GROUP_CONCAT)

当前数据状态:见上文 employees 表完整数据。

执行语句:

SELECT
    dept,
    GROUP_CONCAT(emp_name ORDER BY emp_name ASC SEPARATOR '、') AS 员工名单
FROM employees
GROUP BY dept;

操作后结果:

dept员工名单
技术部大翔、白歌

结果解读:GROUP_CONCAT 将同一组内的多行字符串拼接起来。ORDER BY 控制拼接顺序(按姓名升序),SEPARATOR 指定分隔符为 '、'。默认分隔符是逗号,最大长度受 group_concat_max_len 系统变量限制(默认 1024 字节)。当前表中所有员工都在技术部,分组后只有 1 行。

再看一个带 DISTINCT 的例子:

SELECT
    GROUP_CONCAT(DISTINCT dept ORDER BY dept ASC SEPARATOR ' | ') AS 所有部门
FROM employees;

操作后结果:

所有部门
技术部

结果解读:DISTINCT 先去重,再拼接。注意这里不需要 GROUP BY,因为是对全表做聚合。

场景三:聚合函数与 NULL 的陷阱

当前数据状态:见上文 employees 表完整数据(2 行,其中 1 行 score 为 NULL:白歌)。

执行语句:

SELECT
    COUNT(*)     AS 总行数,
    COUNT(score) AS 非NULL行数,
    SUM(score)   AS 总和,
    AVG(score)   AS 平均值,
    AVG(IFNULL(score, 0)) AS 含NULL平均值,
    MAX(score)   AS 最大值,
    MIN(score)   AS 最小值
FROM employees;

操作后结果:

总行数非NULL行数总和平均值含NULL平均值最大值最小值
21100100.000050.0000100100

结果解读:

  • COUNT(*) = 2:统计所有行
  • COUNT(score) = 1:忽略 NULL,只统计非 NULL 行
  • SUM(score) = 100:忽略 NULL
  • AVG(score) = 100.0000:100 / 1 = 100.0000,忽略 NULL 后除以非 NULL 行数
  • AVG(IFNULL(score, 0)) = 50.0000:(100+0)/2 = 50.0000,NULL 被替换为 0 后参与计算
  • MAX(score) = 100:忽略 NULL 后的最大值
  • MIN(score) = 100:忽略 NULL 后的最小值(唯一非 NULL 值)

关键区别:AVG(score) 忽略 NULL 后除以非 NULL 行数;若希望 NULL 视为 0,需用 AVG(IFNULL(score, 0))。两者的语义完全不同,使用时要根据业务需求选择。

场景四:聚合函数与 WHERE 结合

当前数据状态:见上文 employees 表完整数据。

执行语句:

SELECT
    COUNT(*) AS 技术部人数,
    AVG(score) AS 技术部平均分,
    MAX(score) AS 技术部最高分
FROM employees
WHERE dept = '技术部';

操作后结果:

技术部人数技术部平均分技术部最高分
2100.0000100.00

结果解读:WHERE 先过滤出技术部的员工(大翔、白歌),然后聚合函数在这 2 行上计算。白歌的 score 为 NULL,因此 AVG 计算为 100/1 = 100.00,除以 1(非 NULL 行数)而非 2。

场景五:同时统计多个维度(CASE WHEN + 聚合)

当前数据状态:见上文 employees 表完整数据。

执行语句:

SELECT
    COUNT(*) AS 总人数,
    COUNT(CASE WHEN dept = '技术部' THEN 1 END) AS 技术部人数,
    COUNT(CASE WHEN dept = '产品部' THEN 1 END) AS 产品部人数,
    COUNT(CASE WHEN score >= 90 THEN 1 END) AS 优秀人数,
    COUNT(CASE WHEN score IS NULL THEN 1 END) AS 未录入人数
FROM employees;

操作后结果:

总人数技术部人数产品部人数优秀人数未录入人数
22011

结果解读:CASE WHEN 配合 COUNT 是单查询实现条件统计的经典技巧。不满足条件的行返回 NULL,COUNT 忽略 NULL,因此只统计满足条件的行。

  • 总人数:2
  • 技术部人数:大翔、白歌 = 2
  • 产品部人数:0(没有产品部员工)
  • 优秀人数(score >= 90):大翔 100 = 1(白歌 NULL 不满足)
  • 未录入人数(score IS NULL):白歌 = 1

场景六:空表和全 NULL 的情况

当前数据状态:创建一张空表和一张全 NULL 表进行演示。

CREATE TABLE IF NOT EXISTS empty_table (
    id INT,
    val INT
);

CREATE TABLE IF NOT EXISTS all_null (
    id INT,
    val INT
);
INSERT INTO all_null (id, val) VALUES (1, NULL), (2, NULL);

执行语句(空表):

SELECT
    COUNT(*) AS cnt,
    COUNT(val) AS cnt_val,
    SUM(val) AS sum_val,
    AVG(val) AS avg_val,
    MAX(val) AS max_val,
    MIN(val) AS min_val
FROM empty_table;

操作后结果:

cntcnt_valsum_valavg_valmax_valmin_val
00NULLNULLNULLNULL

结果解读:空表没有任何行。COUNT(*) 返回 0,但 SUM、AVG、MAX、MIN 没有任何输入值,因此都返回 NULL。

执行语句(全 NULL 表):

SELECT
    COUNT(*) AS cnt,
    COUNT(val) AS cnt_val,
    SUM(val) AS sum_val,
    AVG(val) AS avg_val,
    MAX(val) AS max_val,
    MIN(val) AS min_val
FROM all_null;

操作后结果:

cntcnt_valsum_valavg_valmax_valmin_val
20NULLNULLNULLNULL

结果解读:all_null 表有 2 行,但 val 全部为 NULL。COUNT(*) = 2(统计行),COUNT(val) = 0(忽略 NULL)。所有其他聚合函数都返回 NULL,因为没有任何有效的输入值。

常见误区

误区正解
COUNT(列) 和 COUNT(*) 一样COUNT(*) 统计所有行;COUNT(列) 忽略 NULL。
聚合函数把 NULL 当 0聚合函数忽略 NULL,不是当 0。
SUM 可以对字符串使用MySQL 会尝试将字符串隐式转为数字,非数字串转为 0,结果不可预期。
MAX/MIN 只能用于数字也可以用于字符串(按字母顺序)和日期。
聚合函数不能和普通列混用不带 GROUP BY 时,聚合函数会对全表汇总为一行,普通列会随机取一行值(MySQL 5.7 默认允许,但结果无意义)。

面试考点

Q:COUNT(*)、COUNT(1)、COUNT(列) 的区别?

COUNT(*) 和 COUNT(1) 在 MySQL 5.7 中优化器会同等对待,都统计所有行;COUNT(列) 统计该列非 NULL 的行数。

Q:聚合函数的结果可以是 NULL 吗?

可以。当没有任何输入行(如空表),或所有值都是 NULL 时,SUM、AVG、MAX、MIN、GROUP_CONCAT 都返回 NULL。COUNT 返回 0。

Q:如何计算"包含 NULL 的平均值"(NULL 视为 0)?

AVG(IFNULL(score, 0)) 或 AVG(COALESCE(score, 0))。注意这与默认 AVG(score) 的语义完全不同。

Q:GROUP_CONCAT 有长度限制吗?

有。默认最大长度由 group_concat_max_len 系统变量控制(默认 1024 字节)。超出限制会被截断,可以通过 SET group_concat_max_len = 100000; 调整。

小结

  • 聚合函数将多行汇总为单一值
  • COUNT(*) 统计行数,COUNT(列) 统计非 NULL 行数
  • 所有聚合函数(除 COUNT(*))都忽略 NULL
  • GROUP_CONCAT 是多行转字符串的利器
  • 聚合函数在空表或全 NULL 情况下可能返回 NULL(COUNT 返回 0)
  • CASE WHEN + 聚合函数可以实现单查询多维度统计

下一章引子:聚合函数配合 GROUP BY,就能实现"分组统计"——这是数据分析中最常用的 SQL 模式之一。

上一页
COUNT
下一页
比较运算符