源本科技 | 码上会

MySQL DQL 聚合、分组与分页

2026/03/14
49
0

引言

在之前的学习中,我们掌握了如何检索单行或多行数据(SELECT ... WHERE)。但在实际业务中,老板或用户往往不关心“某一个人的成绩”,而更关心整体统计信息,例如:

  • “全班数学平均分是多少?”

  • “男生和女生哪个群体的英语更好?”

  • “如何在一万条数据中只展示第一页的 10 条?”

这就需要用到 DQL 的高级功能:聚合函数分组查询分页查询。它们是将原始数据转化为商业智能(BI)的关键工具。


聚合函数

什么是聚合函数

聚合函数是对一组值(纵向的一列)执行计算,并返回单个值的函数。

  • 形象理解:把一列数据看作一个整体,进行“压缩”计算。

  • 典型场景:统计总数、求和、算平均分、找最高 / 最低分。

数据准备

为了演示,我们创建一张 student 表,并插入模拟数据:

CREATE TABLE student (
    id INT AUTO_INCREMENT PRIMARY KEY,
    `name` VARCHAR(50),
    age INT,
    sex VARCHAR(10),
    address VARCHAR(50),
    math INT,
    english INT,
    hire_date DATE
);

INSERT INTO student (`name`, age, sex, address, math, english, hire_date) VALUES
    ('李星云', 20, '男', '列车', 85, 90, '2022-01-01'),
    ('三月七', 22, '女', '列车', 78, 95, '2022-02-15'),
    ('罗刹', 21, '男', '罗浮', 92, 88, '2022-03-10'),
    ('镜流', 23, '女', '罗浮', 76, 94, '2022-04-20'),
    ('符玄', 22, '女', '罗浮', 89, 82, '2022-05-05'),
    ('寒鸦', 24, '女', '仙舟', 95, 87, '2022-06-15'),
    ('藿藿', 21, '女', '仙舟', 80, 91, '2022-07-25'),
    ('阮梅', 23, '女', '罗浮', 88, 84, '2022-08-30'),
    ('刃', 22, '男', '星穹', 91, 89, '2022-09-10'),
    ('丹恒', 24, '男', '星穹', 84, 93, '2022-10-15');

常用聚合函数

函数名

描述

适用数据类型

注意事项

COUNT(*)

统计总行数

所有类型

包含 NULL 值,效率最高

COUNT(col)

统计某列非空行数

所有类型

忽略 NULL

SUM(col)

求和

数值型

忽略 NULL

AVG(col)

求平均值

数值型

忽略 NULL

MAX(col)

求最大值

数值 / 字符串 / 日期

忽略 NULL

MIN(col)

求最小值

数值 / 字符串 / 日期

忽略 NULL

实战演练

场景 A

班级基本概况

需求:统计班级一共有多少个学生?

SELECT COUNT(*) AS total_students FROM student;
-- 结果:10

场景 B

数学成绩分析

需求:查询数学成绩的最高分、最低分、总分和平均分。

SELECT 
    MAX(math) AS max_score,
    MIN(math) AS min_score,
    SUM(math) AS total_score,
    AVG(math) AS avg_score
FROM student;

场景 C

英语最低分

需求:找出英语考得最低的是多少分?

SELECT MIN(english) FROM student;
-- 结果:82 (符玄)

分组查询

为什么要分组

如果我们需要知道“男生的平均分”和“女生的平均分”,直接用 AVG(math) 只能得到全班的平均分。
分组查询的核心思想是:先分类,再统计

  • 将数据按照某个字段(如 sex)切成不同的组。

  • 对每个组单独执行聚合函数。

2. 语法结构

SELECT 分组字段, 聚合函数(列名) 
FROM 表名 
[WHERE 前置条件] 
GROUP BY 分组字段 
[HAVING 后置条件];

重要规则
GROUP BY 之后,SELECT 子句中出现的字段必须满足以下两个条件之一:

  1. 出现在 GROUP BY 后面的分组字段。

  2. 被聚合函数包裹(如 COUNT, AVG)。

实战演练

场景 A

男女生数学平均分

需求:分别计算男同学和女同学的数学平均分。

SELECT sex, AVG(math) AS avg_math
FROM student
GROUP BY sex;

场景 B

平均分 + 人数统计

需求:查询男女生各自的数学平均分,以及各自的人数。

SELECT 
    sex, 
    AVG(math) AS avg_math, 
    COUNT(*) AS student_count
FROM student
GROUP BY sex;

场景 C

高分段筛选

需求:只统计数学成绩高于 90 分的学生,计算男女生的平均分和人数。

SELECT 
    sex, 
    AVG(math) AS avg_math, 
    COUNT(*) AS student_count
FROM student
WHERE math > 90  -- 第一步:先过滤掉低于 90 分的行
GROUP BY sex;    -- 第二步:对剩下的行进行分组统计

数据流向:原始数据 -> WHERE 过滤 -> 剩余数据 -> GROUP BY 分组 -> 聚合计算

场景 D

分组后的二次筛选

需求:统计数学成绩高于 90 分的学生,按性别分组,只显示人数大于 1 人的组。

SELECT 
    sex, 
    AVG(math) AS avg_math, 
    COUNT(*) AS student_count
FROM student
WHERE math > 90          -- 1. 行级过滤:只留 >90 分的记录
GROUP BY sex             -- 2. 分组:按性别归类
HAVING COUNT(*) > 1;     -- 3. 组级过滤:只留人数 >1 的组

WHERE vs HAVING

这是面试和考试的高频考点,两者的区别至关重要:

特性

WHERE

HAVING

过滤时机

分组前过滤

分组后过滤

作用对象

原始数据行 (Rows)

分组后的结果集 (Groups)

能否用聚合函数

不能 ( 如 WHERE COUNT(*) > 1 报错 )

( 如 HAVING COUNT(*) > 1 合法 )

性能影响

先过滤再分组,效率高 (推荐优先使用)

先分组再过滤,开销稍大

执行顺序

FROM -> WHERE -> GROUP BY -> HAVING

同上

记忆口诀
“先 WHERE 挑好人,再 GROUP 分好组,最后 HAVING 挑好组。”


分页查询

为什么需要分页

当表中有成千上万条数据时,一次性全部查询出来会导致:

  • 内存溢出:客户端无法承载大量数据。

  • 网络拥堵:传输时间过长。

  • 体验极差:用户无法在屏幕上查看完所有数据。

解决方案:每次只查一页(例如每页 10 条),用户点击“下一页”再查接下来的 10 条。

语法格式

MySQL 使用 LIMIT 关键字实现分页:

SELECT 字段列表 FROM 表名 LIMIT 起始索引, 每页条数;
  • 起始索引 (Offset):从 0 开始计数。

    • 第 1 页:从 0 开始。

    • 第 2 页:从 每页条数 开始。

  • 每页条数 (Limit):固定大小。

起始索引计算公式

假设:

  • 当前页码为 currentPage (从 1 开始)

  • 每页显示 pageSize

起始索引=(currentPage1)×pageSize起始索引 =(currentPage−1)×pageSize

实战演练

假设每页显示 3 条数据 (pageSize = 3):

查询第 1 页

  • 公式:(1 - 1) * 3 = 0

SELECT * FROM student LIMIT 0, 3;
-- 获取:第 1, 2, 3 条

查询第 2 页

  • 公式:(2 - 1) * 3 = 3

SELECT * FROM student LIMIT 3, 3;
-- 获取:第 4, 5, 6 条 (跳过前 3 条)

查询第 3 页

  • 公式:(3 - 1) * 3 = 6

SELECT * FROM student LIMIT 6, 3;
-- 获取:第 7, 8, 9 条 (跳过前 6 条)

简化写法

如果从第 0 条开始查,LIMIT 后面可以只跟一个数字:

SELECT * FROM student LIMIT 3; -- 等同于 LIMIT 0, 3

SQL 语句的完整执行顺序

当你写下一条复杂的 SQL 时,数据库内部是这样执行的:

  1. FROM: 找到表。

  2. WHERE: 过滤行(排除不满足条件的数据)。

  3. GROUP BY: 将剩余数据分组。

  4. HAVING: 过滤组(排除不满足条件的组)。

  5. SELECT: 选择要显示的列(执行聚合函数)。

  6. ORDER BY: 对结果排序(本章节未详细展开,但通常在 SELECT 之后)。

  7. LIMIT: 截取分页数据。