-- 创建学生表
CREATE TABLE students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age TINYINT UNSIGNED,
class_id INT
);
-- 创建课程表
CREATE TABLE courses (
id INT PRIMARY KEY AUTO_INCREMENT,
course_name VARCHAR(100) NOT NULL,
credit TINYINT UNSIGNED
);
-- 创建成绩表
CREATE TABLE scores (
id INT PRIMARY KEY AUTO_INCREMENT,
student_id INT,
course_id INT,
score DECIMAL(5, 2),
FOREIGN KEY (student_id) REFERENCES students(id),
FOREIGN KEY (course_id) REFERENCES courses(id)
);
-- 创建班级表
CREATE TABLE classes (
id INT PRIMARY KEY AUTO_INCREMENT,
class_name VARCHAR(50) NOT NULL,
teacher VARCHAR(50)
);
-- 插入测试数据
INSERT INTO classes (class_name, teacher) VALUES
('计算机1班', '张老师'),
('计算机2班', '李老师'),
('计算机3班', '王老师');
INSERT INTO students (name, age, class_id) VALUES
('张三', 20, 1),
('李四', 22, 1),
('王五', 21, 2),
('赵六', 19, 2),
('孙七', 23, 3),
('周八', 20, NULL);
INSERT INTO courses (course_name, credit) VALUES
('数据库原理', 4),
('数据结构', 4),
('操作系统', 3),
('计算机网络', 3);
INSERT INTO scores (student_id, course_id, score) VALUES
(1, 1, 85.5),
(1, 2, 92.0),
(2, 1, 78.5),
(2, 2, 88.0),
(3, 1, 95.5),
(3, 3, 82.0),
(4, 2, 90.0),
(5, 1, 88.0);
| 连接类型 | 说明 | 关键字 |
|---|---|---|
| 内连接 | 只返回匹配的行 | INNER JOIN |
| 左连接 | 返回左表所有行 | LEFT JOIN |
| 右连接 | 返回右表所有行 | RIGHT JOIN |
| 交叉连接 | 笛卡尔积 | CROSS JOIN |
| 自连接 | 表连接自身 | - |
语法格式:
SELECT 列名
FROM 表1
INNER JOIN 表2 ON 表1.列 = 表2.列;
或
SELECT 列名
FROM 表1, 表2
WHERE 表1.列 = 表2.列;
示例:
-- 查询学生及其班级信息
SELECT s.name, s.age, c.class_name
FROM students s
INNER JOIN classes c ON s.class_id = c.id;
-- 使用WHERE语法
SELECT s.name, s.age, c.class_name
FROM students s, classes c
WHERE s.class_id = c.id;
输出:
+--------+------+---------------+
| name | age | class_name |
+--------+------+---------------+
| 张三 | 20 | 计算机1班 |
| 李四 | 22 | 计算机1班 |
| 王五 | 21 | 计算机2班 |
| 赵六 | 19 | 计算机2班 |
| 孙七 | 23 | 计算机3班 |
+--------+------+---------------+
示例:
-- 查询学生、课程和成绩
SELECT s.name, c.course_name, sc.score
FROM students s
INNER JOIN scores sc ON s.id = sc.student_id
INNER JOIN courses c ON sc.course_id = c.id;
输出:
+--------+-----------------+-------+
| name | course_name | score |
+--------+-----------------+-------+
| 张三 | 数据库原理 | 85.50 |
| 张三 | 数据结构 | 92.00 |
| 李四 | 数据库原理 | 78.50 |
| 李四 | 数据结构 | 88.00 |
| 王五 | 数据库原理 | 95.50 |
| 王五 | 操作系统 | 82.00 |
| 赵六 | 数据结构 | 90.00 |
| 孙七 | 数据库原理 | 88.00 |
+--------+-----------------+-------+
-- 使用ON指定连接条件
SELECT s.name, c.class_name
FROM students s
INNER JOIN classes c ON s.class_id = c.id;
语法格式:
SELECT 列名
FROM 表1
INNER JOIN 表2 USING(列名);
示例:
-- 两表有相同列名时使用USING
SELECT s.name, c.class_name
FROM students s
INNER JOIN classes c USING(id);
-- 多个连接条件
SELECT s.name, c.class_name
FROM students s
INNER JOIN classes c
ON s.class_id = c.id
AND c.teacher = '张老师';
语法格式:
SELECT 列名
FROM 表1
LEFT JOIN 表2 ON 表1.列 = 表2.列;
特点:
示例:
-- 查询所有学生及其班级(包括没有班级的学生)
SELECT s.name, s.age, c.class_name
FROM students s
LEFT JOIN classes c ON s.class_id = c.id;
输出:
+--------+------+---------------+
| name | age | class_name |
+--------+------+---------------+
| 张三 | 20 | 计算机1班 |
| 李四 | 22 | 计算机1班 |
| 王五 | 21 | 计算机2班 |
| 赵六 | 19 | 计算机2班 |
| 孙七 | 23 | 计算机3班 |
| 周八 | 20 | NULL |
+--------+------+---------------+
-- 查询没有班级的学生
SELECT s.name, s.age
FROM students s
LEFT JOIN classes c ON s.class_id = c.id
WHERE c.id IS NULL;
输出:
+--------+------+
| name | age |
+--------+------+
| 周八 | 20 |
+--------+------+
语法格式:
SELECT 列名
FROM 表1
RIGHT JOIN 表2 ON 表1.列 = 表2.列;
特点:
示例:
-- 查询所有班级及其学生(包括没有学生的班级)
SELECT s.name, c.class_name
FROM students s
RIGHT JOIN classes c ON s.class_id = c.id;
输出:
+--------+---------------+
| name | class_name |
+--------+---------------+
| 张三 | 计算机1班 |
| 李四 | 计算机1班 |
| 王五 | 计算机2班 |
| 赵六 | 计算机2班 |
| 孙七 | 计算机3班 |
| NULL | 计算机3班 |
+--------+---------------+
MySQL不支持FULL OUTER JOIN,使用UNION模拟:
-- 模拟全连接
SELECT s.name, c.class_name
FROM students s
LEFT JOIN classes c ON s.class_id = c.id
UNION
SELECT s.name, c.class_name
FROM students s
RIGHT JOIN classes c ON s.class_id = c.id;
自连接:一个表与自身连接,用于比较同一表中的行。
-- 创建员工表
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
manager_id INT
);
INSERT INTO employees VALUES
(1, '张总', NULL),
(2, '李经理', 1),
(3, '王经理', 1),
(4, '赵主管', 2),
(5, '孙主管', 3),
(6, '周员工', 4);
-- 查询员工及其直接上级
SELECT
e.name AS employee,
m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
输出:
+-----------+-----------+
| employee | manager |
+-----------+-----------+
| 张总 | NULL |
| 李经理 | 张总 |
| 王经理 | 张总 |
| 赵主管 | 李经理 |
| 孙主管 | 王经理 |
| 周员工 | 赵主管 |
+-----------+-----------+
-- 查询同一经理下的员工
SELECT
e1.name AS employee1,
e2.name AS employee2,
e1.manager_id
FROM employees e1
INNER JOIN employees e2
ON e1.manager_id = e2.manager_id
AND e1.id < e2.id;
输出:
+-----------+-----------+------------+
| employee1 | employee2 | manager_id |
+-----------+-----------+------------+
| 李经理 | 王经理 | 1 |
+-----------+-----------+------------+
子查询:嵌套在其他查询中的查询,也称为内部查询。
| 类型 | 说明 | 位置 |
|---|---|---|
| 标量子查询 | 返回单个值 | SELECT、WHERE |
| 列子查询 | 返回一列 | WHERE |
| 行子查询 | 返回一行 | WHERE |
| 表子查询 | 返回多行多列 | FROM |
示例:
-- 查询成绩高于平均成绩的学生
SELECT name, score
FROM students s
INNER JOIN scores sc ON s.id = sc.student_id
WHERE sc.score > (
SELECT AVG(score) FROM scores
);
输出:
+--------+-------+
| name | score |
+--------+-------+
| 张三 | 92.00 |
| 王五 | 95.50 |
| 赵六 | 90.00 |
+--------+-------+
-- 查询学生成绩与平均分的差值
SELECT
s.name,
sc.score,
(SELECT AVG(score) FROM scores) AS avg_score,
sc.score - (SELECT AVG(score) FROM scores) AS diff
FROM students s
INNER JOIN scores sc ON s.id = sc.student_id;
使用IN:
-- 查询有成绩的学生
SELECT * FROM students
WHERE id IN (
SELECT DISTINCT student_id FROM scores
);
使用NOT IN:
-- 查询没有成绩的学生
SELECT * FROM students
WHERE id NOT IN (
SELECT DISTINCT student_id FROM scores
);
ANY:满足任一值
-- 查询成绩大于课程1的最低分的学生
SELECT s.name, sc.score
FROM students s
INNER JOIN scores sc ON s.id = sc.student_id
WHERE sc.score > ANY (
SELECT score FROM scores WHERE course_id = 1
);
ALL:满足所有值
-- 查询成绩大于课程1的所有成绩的学生
SELECT s.name, sc.score
FROM students s
INNER JOIN scores sc ON s.id = sc.student_id
WHERE sc.score > ALL (
SELECT score FROM scores WHERE course_id = 1
);
示例:
-- 查询与张三同龄同班的学生
SELECT * FROM students
WHERE (age, class_id) = (
SELECT age, class_id
FROM students
WHERE name = '张三'
);
语法格式:
SELECT 列名
FROM (子查询) AS 别名
[WHERE 条件];
示例:
-- 查询每个班级的平均成绩
SELECT c.class_name, avg_score.avg
FROM classes c
INNER JOIN (
SELECT s.class_id, AVG(sc.score) AS avg
FROM students s
INNER JOIN scores sc ON s.id = sc.student_id
GROUP BY s.class_id
) avg_score ON c.id = avg_score.class_id;
语法格式:
WHERE EXISTS (子查询)
特点:
示例:
-- 查询有成绩的学生
SELECT * FROM students s
WHERE EXISTS (
SELECT 1 FROM scores sc
WHERE sc.student_id = s.id
);
-- 查询没有成绩的学生
SELECT * FROM students s
WHERE NOT EXISTS (
SELECT 1 FROM scores sc
WHERE sc.student_id = s.id
);
语法格式:
SELECT 列名 FROM 表1
UNION
SELECT 列名 FROM 表2;
特点:
示例:
-- 查询所有教师和学生姓名
SELECT teacher AS name FROM classes
UNION
SELECT name FROM students;
输出:
+-----------+
| name |
+-----------+
| 张老师 |
| 李老师 |
| 王老师 |
| 张三 |
| 李四 |
| 王五 |
| 赵六 |
| 孙七 |
| 周八 |
+-----------+
语法格式:
SELECT 列名 FROM 表1
UNION ALL
SELECT 列名 FROM 表2;
示例:
-- 查询所有教师和学生姓名(包含重复)
SELECT teacher AS name FROM classes
UNION ALL
SELECT name FROM students;
| 特性 | UNION | JOIN |
|---|---|---|
| 作用 | 合并结果集 | 连接表 |
| 行数 | 相加 | 可能减少或增加 |
| 列数 | 相同 | 相加 |
| 去重 | UNION去重 | 不去重 |
-- 在连接字段上创建索引
CREATE INDEX idx_class_id ON students(class_id);
CREATE INDEX idx_student_id ON scores(student_id);
CREATE INDEX idx_course_id ON scores(course_id);
优化原则:
示例:
-- 小表驱动大表
SELECT s.name, c.class_name
FROM classes c -- 小表
INNER JOIN students s ON s.class_id = c.id; -- 大表
-- 先过滤再连接
SELECT s.name, c.class_name
FROM students s
INNER JOIN classes c ON s.class_id = c.id
WHERE s.age > 20;
EXPLAIN SELECT s.name, c.class_name
FROM students s
INNER JOIN classes c ON s.class_id = c.id;
输出:
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------------+------+----------+-------+
| 1 | SIMPLE | s | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | NULL |
| 1 | SIMPLE | c | NULL | eq_ref | PRIMARY | PRIMARY | 4 | school.s.class_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------------+------+----------+-------+
✅ INNER JOIN返回匹配行 ✅ LEFT JOIN返回左表所有行 ✅ 自连接用于比较同一表中的行 ✅ 子查询可以嵌套在SELECT、WHERE、FROM中 ✅ UNION合并多个查询结果
完成本章学习后,请确认您能够: