前言
在面试中以及实际开发中,多表联查是每个程序员必备技能,下文通过最简单的学生表和课程表的实例帮大家最快入门多表联查技能。
建立数据表
1. 学生表(students)
创建学生表
CREATE TABLE students (
student_id INT AUTO_INCREMENT PRIMARY KEY,
student_name VARCHAR(50) NOT NULL,
gender ENUM('男','女') DEFAULT NULL,
age TINYINT UNSIGNED
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入学生数据(20条)
INSERT INTO students (student_name, gender, age) VALUES
('李明', '男', 18), ('王芳', '女', 19), ('张伟', '男', 20),
('赵敏', '女', 18), ('陈强', '男', 19), ('周雪', '女', 20),
('黄磊', '男', 21), ('吴倩', '女', 19), ('徐洋', '男', 18),
('孙莉', '女', 20), ('马超', '男', 19), ('朱婷', '女', 21),
('何军', '男', 18), ('林琳', '女', 20), ('郑凯', '男', 19),
('胡月', '女', 21), ('郭涛', '男', 18), ('高菲', '女', 20),
('曹阳', '男', 19), ('丁薇', '女', 18);
得到以下结果
2. 课程表 (courses)
创建课程表
CREATE TABLE courses (
course_id INT AUTO_INCREMENT PRIMARY KEY,
course_name VARCHAR(50) NOT NULL,
credit TINYINT UNSIGNED
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入课程数据(20条)
INSERT INTO courses (course_name, credit) VALUES
('高等数学', 4), ('大学英语', 3), ('计算机基础', 2),
('数据结构', 4), ('操作系统', 3), ('数据库原理', 4),
('计算机网络', 3), ('软件工程', 2), ('人工智能', 4),
('数字电路', 3), ('编译原理', 4), ('算法设计', 3),
('图形学', 2), ('机器学习', 4), ('网络安全', 3),
('移动开发', 2), ('大数据分析', 4), ('物联网技术', 3),
('嵌入式系统', 2), ('计算机组成', 4);
得到以下结果
3. 选课关系表 (student_courses)
创建选课关系表
CREATE TABLE student_courses (
student_id INT NOT NULL,
course_id INT NOT NULL,
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES students(student_id) ON DELETE CASCADE,
FOREIGN KEY (course_id) REFERENCES courses(course_id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入选课关系
INSERT INTO student_courses (student_id, course_id) VALUES
(1,1), (1,3), (1,15),
(2,2), (2,14), (2,6),
(3,5), (3,17), (4,11),
(4,13), (5,5), (7,2),
(9,4), (9,16), (11,5),
(13,17), (15,1), (16,13),
(17,5), (18,12), (19,14),
(19,16), (19,15), (19,17),
(20,17), (20,19);
得到以下结果
多表联查理论及实现
1. 查询学生选课详情
以上需求可以使用隐式的内连接(INNER JOIN),只有当 ON 条件匹配时,记录才会被返回。
如果某个学生没有选课,或者某些课程没有学生选修,这些记录不会出现在结果中。
完整代码如下:
SELECT s.student_name, c.course_name
FROM students s
JOIN student_courses sc ON s.student_id = sc.student_id
JOIN courses c ON sc.course_id = c.course_id;
代码详解:
SELECT s.student_name, c.course_name
这部分指定了查询结果中需要显示的字段:
- s.student_name:从 students 表中获取的学生姓名。
- c.course_name::从 courses 表中获取的课程名称。
别名 s 和 c 分别代表 students 表和 courses 表。
FROM students s
查询的起点是 students 表,并给它指定了一个别名 s,以便在后续代码中简化引用。
JOIN student_courses sc ON s.student_id = sc.student_id
使用 JOIN 将 students 表与 student_courses 表连接起来。
连接条件是 s.student_id = sc.student_id,即学生的 ID 在两个表中必须匹配。
这一步是为了找到每个学生选了哪些课程(通过中间表 student_courses)。
JOIN courses c ON sc.course_id = c.course_id
再次使用 JOIN 将 student_courses 表与 courses 表连接起来。
连接条件是 sc.course_id = c.course_id,即课程的 ID 在两个表中必须匹配。
这一步是为了获取每个课程的具体信息(例如课程名称)。
查询结果
2. 统计课程选修人数
以上需求可以使用 INNER JOIN,结果会返回有学生选修的课程,而未被选修的课程不会出现在结果中。
使用 LEFT JOIN 可以确保所有课程都出现在结果中,即使它们没有被选修。
SELECT c.course_name, COUNT(sc.student_id) AS student_count
FROM courses c
LEFT JOIN student_courses sc ON c.course_id = sc.course_id
GROUP BY c.course_id;
代码详解:
SELECT c.course_name, COUNT(sc.student_id) AS student_count
这部分指定了查询结果中需要显示的字段:
- c.course_name::从 courses 表中获取的课程名称。
- COUNT(sc.student_id):统计每个课程的学生数量。
- sc.student_id 是 student_courses 表中的字段,表示选修该课程的学生 ID。
如果某课程没有任何学生选修,则 sc.student_id 的值为 NULL,COUNT 函数会忽略 NULL 值,因此返回的结果是 0。
别名 student_count 用于给统计结果命名。
FROM courses c
查询的起点是 courses 表,并给它指定了一个别名 c,以便在后续代码中简化引用。
LEFT JOIN student_courses sc ON c.course_id = sc.course_id
使用 LEFT JOIN 将 courses 表与 student_courses 表连接起来。
连接条件是 c.course_id = sc.course_id,即课程的 ID 在两个表中必须匹配。
LEFT JOIN 的特点:
它会保留左表(courses 表)的所有记录,即使右表(student_courses 表)中没有匹配的记录。
如果某课程没有任何学生选修,sc.student_id 的值将为 NULL,但该课程仍会出现在结果中。
GROUP BY c.course_id
按照 c.course_id 对结果进行分组。
每个课程对应一组数据,COUNT(sc.student_id) 会统计每个课程的学生数量。
查询结果
3. 查询未选课学生
SELECT s.*
FROM students s
LEFT JOIN student_courses sc ON s.student_id = sc.student_id
WHERE sc.course_id IS NULL;
代码详解
SELECT s.*
这部分指定了查询结果中需要显示的字段。
使用 s.* 表示从 students 表中获取所有字段(即所有学生信息)。
别名 s 是 students 表的简写。
FROM students s
查询的起点是 students 表,并给它指定了一个别名 s,以便在后续代码中简化引用。
LEFT JOIN student_courses sc ON s.student_id = sc.student_id
使用 LEFT JOIN 将 students 表与 student_courses 表连接起来。
连接条件是 s.student_id = sc.student_id,即学生的 ID 在两个表中必须匹配。
LEFT JOIN 的特点:
它会保留左表(students 表)的所有记录,即使右表(student_courses 表)中没有匹配的记录。
如果某学生没有选修任何课程,则 sc.course_id 的值将为 NULL。
WHERE sc.course_id IS NULL
过滤条件是 sc.course_id IS NULL,即只选择那些在 student_courses 表中没有匹配记录的学生。
换句话说,这部分筛选出的是没有选修任何课程的学生
查询结果