在之前的学习中,我们掌握了如何检索单行或多行数据(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');班级基本概况
需求:统计班级一共有多少个学生?
SELECT COUNT(*) AS total_students FROM student;
-- 结果:10数学成绩分析
需求:查询数学成绩的最高分、最低分、总分和平均分。
SELECT
MAX(math) AS max_score,
MIN(math) AS min_score,
SUM(math) AS total_score,
AVG(math) AS avg_score
FROM student;英语最低分
需求:找出英语考得最低的是多少分?
SELECT MIN(english) FROM student;
-- 结果:82 (符玄)如果我们需要知道“男生的平均分”和“女生的平均分”,直接用 AVG(math) 只能得到全班的平均分。
分组查询的核心思想是:先分类,再统计。
将数据按照某个字段(如 sex)切成不同的组。
对每个组单独执行聚合函数。
SELECT 分组字段, 聚合函数(列名)
FROM 表名
[WHERE 前置条件]
GROUP BY 分组字段
[HAVING 后置条件];重要规则:
在GROUP BY之后,SELECT子句中出现的字段必须满足以下两个条件之一:
出现在
GROUP BY后面的分组字段。被聚合函数包裹(如
COUNT,AVG)。
男女生数学平均分
需求:分别计算男同学和女同学的数学平均分。
SELECT sex, AVG(math) AS avg_math
FROM student
GROUP BY sex;平均分 + 人数统计
需求:查询男女生各自的数学平均分,以及各自的人数。
SELECT
sex,
AVG(math) AS avg_math,
COUNT(*) AS student_count
FROM student
GROUP BY sex;高分段筛选
需求:只统计数学成绩高于 90 分的学生,计算男女生的平均分和人数。
SELECT
sex,
AVG(math) AS avg_math,
COUNT(*) AS student_count
FROM student
WHERE math > 90 -- 第一步:先过滤掉低于 90 分的行
GROUP BY sex; -- 第二步:对剩下的行进行分组统计数据流向:原始数据 -> WHERE 过滤 -> 剩余数据 -> GROUP BY 分组 -> 聚合计算
分组后的二次筛选
需求:统计数学成绩高于 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挑好人,再GROUP分好组,最后HAVING挑好组。”
当表中有成千上万条数据时,一次性全部查询出来会导致:
内存溢出:客户端无法承载大量数据。
网络拥堵:传输时间过长。
体验极差:用户无法在屏幕上查看完所有数据。
解决方案:每次只查一页(例如每页 10 条),用户点击“下一页”再查接下来的 10 条。
MySQL 使用 LIMIT 关键字实现分页:
SELECT 字段列表 FROM 表名 LIMIT 起始索引, 每页条数;起始索引 (Offset):从 0 开始计数。
第 1 页:从 0 开始。
第 2 页:从 每页条数 开始。
每页条数 (Limit):固定大小。
假设:
当前页码为 currentPage (从 1 开始)
每页显示 pageSize 条
假设每页显示 3 条数据 (pageSize = 3):
公式:(1 - 1) * 3 = 0
SELECT * FROM student LIMIT 0, 3;
-- 获取:第 1, 2, 3 条公式:(2 - 1) * 3 = 3
SELECT * FROM student LIMIT 3, 3;
-- 获取:第 4, 5, 6 条 (跳过前 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 时,数据库内部是这样执行的:
FROM: 找到表。
WHERE: 过滤行(排除不满足条件的数据)。
GROUP BY: 将剩余数据分组。
HAVING: 过滤组(排除不满足条件的组)。
SELECT: 选择要显示的列(执行聚合函数)。
ORDER BY: 对结果排序(本章节未详细展开,但通常在 SELECT 之后)。
LIMIT: 截取分页数据。