第 4 章:SELECT 进阶查询
在前几章中,我们学习了基本的 SELECT 查询。现在我们将深入学习更高级的查询技巧,包括聚合函数、分组查询、条件筛选等,这些是数据分析和报表生成的基础。
本章内容概要
- 聚合函数(COUNT、SUM、AVG、MAX、MIN)
- GROUP BY 分组查询
- HAVING 子句
- DISTINCT 去重
- LIKE 模糊查询
- BETWEEN 范围查询
- IN 列表查询
- IS NULL 判断空值
- 实战练习:学生成绩统计分析
聚合函数
聚合函数对一组值执行计算并返回单个值。
COUNT 函数
COUNT 函数用于计算行数。
sql
USE student_management;
-- 计算学生总数
SELECT COUNT(*) AS 学生总数 FROM students;
-- 计算有邮箱的学生数量
SELECT COUNT(email) AS 有邮箱学生数 FROM students;
-- 计算不同专业的数量
SELECT COUNT(DISTINCT major) AS 专业数 FROM students;SUM 函数
SUM 函数用于计算数值列的总和。
首先,让我们为成绩表添加一些测试数据:
sql
-- 插入课程数据(如果还没有)
INSERT INTO courses (course_code, course_name, credits, description, teacher)
VALUES
('CS101', '计算机基础', 3, '计算机科学入门课程', '张教授'),
('CS102', '数据结构', 4, '数据结构与算法分析', '李教授'),
('CS103', '数据库原理', 3, '关系数据库设计与实现', '王教授'),
('CS104', '操作系统', 4, '操作系统原理与实践', '赵教授');
-- 插入成绩数据
INSERT INTO grades (student_id, course_id, grade, exam_date, semester)
VALUES
(1, 1, 85.5, '2023-12-15', '2023秋季'),
(1, 2, 78.0, '2023-12-20', '2023秋季'),
(2, 1, 92.0, '2023-12-15', '2023秋季'),
(2, 3, 88.5, '2023-12-22', '2023秋季'),
(3, 2, 76.5, '2023-12-20', '2023秋季'),
(3, 4, 82.0, '2023-12-25', '2023秋季'),
(4, 1, 89.0, '2023-12-15', '2023秋季'),
(4, 4, 91.5, '2023-12-25', '2023秋季');现在可以使用 SUM 函数:
sql
-- 计算所有成绩的总分
SELECT SUM(grade) AS 总分 FROM grades;
-- 计算每个学生的总分
SELECT student_id, SUM(grade) AS 总分
FROM grades
GROUP BY student_id;AVG 函数
AVG 函数用于计算数值列的平均值。
sql
-- 计算所有成绩的平均分
SELECT AVG(grade) AS 平均分 FROM grades;
-- 计算每个学生的平均分
SELECT student_id, AVG(grade) AS 平均分
FROM grades
GROUP BY student_id;MAX 和 MIN 函数
MAX 和 MIN 函数分别用于查找最大值和最小值。
sql
-- 查找最高分和最低分
SELECT MAX(grade) AS 最高分, MIN(grade) AS 最低分 FROM grades;
-- 查找获得最高分的学生
SELECT student_id, MAX(grade) AS 最高分
FROM grades
GROUP BY student_id;GROUP BY 分组查询
GROUP BY 子句用于将结果集按一个或多个列进行分组。
基本分组
sql
-- 按专业分组统计学生人数
SELECT major AS 专业, COUNT(*) AS 人数
FROM students
GROUP BY major;
-- 按性别分组统计学生人数
SELECT gender AS 性别, COUNT(*) AS 人数
FROM students
GROUP BY gender;多列分组
sql
-- 按专业和班级分组统计学生人数
SELECT major AS 专业, class_name AS 班级, COUNT(*) AS 人数
FROM students
GROUP BY major, class_name;分组与聚合函数结合
sql
-- 计算每个学生的各科成绩总分和平均分
SELECT
student_id AS 学生ID,
COUNT(*) AS 选课数,
SUM(grade) AS 总分,
AVG(grade) AS 平均分,
MAX(grade) AS 最高分,
MIN(grade) AS 最低分
FROM grades
GROUP BY student_id;HAVING 子句
HAVING 子句用于对分组后的结果进行筛选。
sql
-- 查找平均分大于85分的学生
SELECT
student_id AS 学生ID,
AVG(grade) AS 平均分
FROM grades
GROUP BY student_id
HAVING AVG(grade) > 85;
-- 查找选课数大于等于2门的学生
SELECT
student_id AS 学生ID,
COUNT(*) AS 选课数
FROM grades
GROUP BY student_id
HAVING COUNT(*) >= 2;
-- 查找有3门以上课程的学期
SELECT
semester AS 学期,
COUNT(*) AS 课程数
FROM grades
GROUP BY semester
HAVING COUNT(*) > 3;DISTINCT 去重
DISTINCT 关键字用于去除查询结果中的重复行。
sql
-- 查看所有不同的专业
SELECT DISTINCT major FROM students;
-- 查看所有不同的学期
SELECT DISTINCT semester FROM grades;
-- 统计不同专业的数量
SELECT COUNT(DISTINCT major) AS 专业数 FROM students;LIKE 模糊查询
LIKE 操作符用于在 WHERE 子句中进行模式匹配。
sql
-- 查找姓张的学生
SELECT * FROM students WHERE name LIKE '张%';
-- 查找名字中包含"三"的学生
SELECT * FROM students WHERE name LIKE '%三%';
-- 查找名字是两个字的学生
SELECT * FROM students WHERE name LIKE '__';
-- 查找邮箱是 example.com 域名的学生
SELECT * FROM students WHERE email LIKE '%@example.com';
-- 使用 NOT LIKE 查找不满足条件的记录
SELECT * FROM students WHERE name NOT LIKE '张%';BETWEEN 范围查询
BETWEEN 操作符用于选取介于两个值之间的数据。
sql
-- 查找出生日期在2005年1月1日到2005年12月31日之间的学生
SELECT * FROM students
WHERE birth_date BETWEEN '2005-01-01' AND '2005-12-31';
-- 查找成绩在80到90之间的记录
SELECT * FROM grades
WHERE grade BETWEEN 80 AND 90;
-- 查找入学日期在指定范围内的学生
SELECT * FROM students
WHERE enrollment_date BETWEEN '2023-09-01' AND '2023-09-30';IN 列表查询
IN 操作符允许在 WHERE 子句中指定多个可能的值。
sql
-- 查找计算机科学与技术或软件工程专业的学生
SELECT * FROM students
WHERE major IN ('计算机科学与技术', '软件工程');
-- 查找特定课程ID的成绩
SELECT * FROM grades
WHERE course_id IN (1, 2, 3);
-- 使用 NOT IN 查找不在列表中的记录
SELECT * FROM students
WHERE major NOT IN ('计算机科学与技术', '软件工程');IS NULL 判断空值
IS NULL 和 IS NOT NULL 用于检测 NULL 值。
sql
-- 查找没有电话号码的学生
SELECT * FROM students WHERE phone IS NULL;
-- 查找有邮箱的学生
SELECT * FROM students WHERE email IS NOT NULL;
-- 查找没有成绩记录的学生
SELECT * FROM students
WHERE id NOT IN (SELECT DISTINCT student_id FROM grades WHERE student_id IS NOT NULL);实战练习:学生成绩统计分析
让我们通过一个完整的学生成绩统计分析来练习这些高级查询技巧。
1. 基础统计分析
sql
-- 查看成绩表中的所有数据
SELECT
s.name AS 学生姓名,
c.course_name AS 课程名称,
g.grade AS 成绩,
g.exam_date AS 考试日期,
g.semester AS 学期
FROM grades g
JOIN students s ON g.student_id = s.id
JOIN courses c ON g.course_id = c.id
ORDER BY g.semester, s.name, c.course_name;2. 学生个人成绩分析
sql
-- 每个学生的成绩统计
SELECT
s.name AS 学生姓名,
COUNT(g.id) AS 选课数,
SUM(g.grade) AS 总分,
AVG(g.grade) AS 平均分,
MAX(g.grade) AS 最高分,
MIN(g.grade) AS 最低分
FROM students s
LEFT JOIN grades g ON s.id = g.student_id
GROUP BY s.id, s.name
ORDER BY 平均分 DESC;3. 课程成绩分析
sql
-- 每门课程的成绩统计
SELECT
c.course_name AS 课程名称,
COUNT(g.id) AS 参考人数,
AVG(g.grade) AS 平均分,
MAX(g.grade) AS 最高分,
MIN(g.grade) AS 最低分
FROM courses c
LEFT JOIN grades g ON c.id = g.course_id
GROUP BY c.id, c.course_name
ORDER BY 平均分 DESC;4. 优秀学生筛选
sql
-- 平均分在85分以上的学生
SELECT
s.name AS 学生姓名,
AVG(g.grade) AS 平均分
FROM students s
JOIN grades g ON s.id = g.student_id
GROUP BY s.id, s.name
HAVING AVG(g.grade) >= 85
ORDER BY 平均分 DESC;5. 成绩分布分析
sql
-- 成绩分布统计
SELECT
CASE
WHEN grade >= 90 THEN '90-100分'
WHEN grade >= 80 THEN '80-89分'
WHEN grade >= 70 THEN '70-79分'
WHEN grade >= 60 THEN '60-69分'
ELSE '60分以下'
END AS 分数段,
COUNT(*) AS 人数
FROM grades
GROUP BY
CASE
WHEN grade >= 90 THEN '90-100分'
WHEN grade >= 80 THEN '80-89分'
WHEN grade >= 70 THEN '70-79分'
WHEN grade >= 60 THEN '60-69分'
ELSE '60分以下'
END
ORDER BY 分数段;6. 学期成绩对比
sql
-- 不同学期的成绩对比
SELECT
semester AS 学期,
COUNT(*) AS 考试人次,
AVG(grade) AS 平均分,
MAX(grade) AS 最高分,
MIN(grade) AS 最低分
FROM grades
GROUP BY semester
ORDER BY semester;7. 综合排名分析
sql
-- 学生综合排名(按平均分)
SELECT
s.name AS 学生姓名,
s.major AS 专业,
COUNT(g.id) AS 选课数,
ROUND(AVG(g.grade), 2) AS 平均分,
RANK() OVER (ORDER BY AVG(g.grade) DESC) AS 排名
FROM students s
JOIN grades g ON s.id = g.student_id
GROUP BY s.id, s.name, s.major
ORDER BY 平均分 DESC;查询优化建议
1. 合理使用索引
对于经常用于 WHERE、ORDER BY、GROUP BY 的列,考虑创建索引:
sql
-- 为成绩表的 student_id 列创建索引
CREATE INDEX idx_grades_student_id ON grades(student_id);
-- 为成绩表的 course_id 列创建索引
CREATE INDEX idx_grades_course_id ON grades(course_id);2. 避免 SELECT *
尽量只查询需要的列,而不是使用 SELECT *:
sql
-- 好的做法
SELECT name, major FROM students WHERE major = '计算机科学与技术';
-- 避免这样做
SELECT * FROM students WHERE major = '计算机科学与技术';3. 使用 LIMIT 限制结果集
当只需要部分结果时,使用 LIMIT:
sql
-- 只查询前10名学生
SELECT name, AVG(grade) AS 平均分
FROM students s
JOIN grades g ON s.id = g.student_id
GROUP BY s.id, s.name
ORDER BY 平均分 DESC
LIMIT 10;小结
在本章中,我们学习了 SELECT 进阶查询的各种技巧:
- 聚合函数 - COUNT、SUM、AVG、MAX、MIN 等函数用于数据统计
- GROUP BY - 按列分组进行统计分析
- HAVING - 对分组结果进行筛选
- DISTINCT - 去除重复记录
- LIKE - 模式匹配查询
- BETWEEN - 范围查询
- IN - 列表查询
- IS NULL - 空值判断
通过这些高级查询技巧,我们可以进行复杂的数据分析和报表生成。在下一章中,我们将学习多表查询与连接操作,这是处理关系型数据库的核心技能。