1. 内连接(INNER JOIN)
1.1 基本概念
内连接是利用WHERE子句对两种表形成的笛卡尔积进行筛选,只返回两个表中连接字段相等的行。
1.2 语法格式
SELECT 字段 FROM 表1 INNER JOIN 表2 ON 连接条件 [AND 其他条件];
1.3 实际案例
显示SMITH的名字和部门名称
传统写法(我们之前学习的方式)
SELECT ename, dname
FROM EMP, DEPT
WHERE EMP.deptno = DEPT.deptno AND ename = 'SMITH';
标准内连接写法
SELECT ename, dname
FROM EMP INNER JOIN DEPT ON EMP.deptno = DEPT.deptno
WHERE ename = 'SMITH';
-- 或者将条件写在ON子句中
SELECT ename, dname
FROM EMP INNER JOIN DEPT ON EMP.deptno = DEPT.deptno AND ename = 'SMITH';
1.4 内连接特点
-
只显示两个表中匹配的记录
-
不匹配的记录不会出现在结果中
-
是开发中最常用的连接方式
2. 外连接(OUTER JOIN)
2.1 外连接概述
外连接分为左外连接和右外连接,用于显示不匹配的记录。
2.2 创建测试数据
-- 学生表
CREATE TABLE stu (id int, name varchar(30));
INSERT INTO stu VALUES (1, 'jack'), (2, 'tom'), (3, 'kity'), (4, 'nono');
-- 成绩表
CREATE TABLE exam (id int, grade int);
INSERT INTO exam VALUES (1, 56), (2, 76), (11, 8);
3. 左外连接(LEFT JOIN)
3.1 基本概念
左外连接会返回左表的所有记录,以及右表中连接字段相等的记录。如果右表没有匹配的记录,则显示为NULL。
3.2 语法格式
SELECT 字段名 FROM 表名1 LEFT JOIN 表名2 ON 连接条件;
3.3 实际案例
查询所有学生的成绩,如果学生没有成绩,也要显示学生信息
SELECT * FROM stu LEFT JOIN exam ON stu.id = exam.id;
查询结果示例:
+------+------+------+-------+
| id | name | id | grade |
+------+------+------+-------+
| 1 | jack | 1 | 56 |
| 2 | tom | 2 | 76 |
| 3 | kity | NULL | NULL | -- 左表有记录,右表无匹配
| 4 | nono | NULL | NULL | -- 左表有记录,右表无匹配
+------+------+------+-------+
3.4 左外连接特点
-
左表记录全部显示
-
右表只显示匹配的记录
-
不匹配的右表字段显示为NULL
4. 右外连接(RIGHT JOIN)
4.1 基本概念
右外连接会返回右表的所有记录,以及左表中连接字段相等的记录。如果左表没有匹配的记录,则显示为NULL。
4.2 语法格式
SELECT 字段 FROM 表名1 RIGHT JOIN 表名2 ON 连接条件;
4.3 实际案例
显示所有成绩信息,即使没有对应的学生信息
SELECT * FROM stu RIGHT JOIN exam ON stu.id = exam.id;
查询结果示例:
+------+------+------+-------+
| id | name | id | grade |
+------+------+------+-------+
| 1 | jack | 1 | 56 |
| 2 | tom | 2 | 76 |
| NULL | NULL | 11 | 8 | -- 右表有记录,左表无匹配
+------+------+------+-------+
5. 综合练习与应用
5.1 实际业务场景
列出部门名称和员工信息,同时显示没有员工的部门
方法一:使用左外连接
SELECT d.dname, e.*
FROM dept d LEFT JOIN emp e ON d.deptno = e.deptno;
方法二:使用右外连接
SELECT d.dname, e.*
FROM emp e RIGHT JOIN dept d ON d.deptno = e.deptno;
5.2 多表外连接应用
-- 显示所有部门、员工及工资等级信息
SELECT d.dname, e.ename, e.sal, s.grade
FROM dept d
LEFT JOIN emp e ON d.deptno = e.deptno
LEFT JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal;
6. 连接类型对比总结
| 连接类型 | 语法 | 结果描述 | 使用场景 |
|---|---|---|---|
| 内连接 | INNER JOIN | 只返回两个表匹配的记录 | 需要完全匹配的数据 |
| 左外连接 | LEFT JOIN | 返回左表全部+右表匹配记录 | 需要显示主表所有记录 |
| 右外连接 | RIGHT JOIN | 返回右表全部+左表匹配记录 | 需要显示从表所有记录 |
7. 实战技巧与注意事项
7.1 性能优化建议
-- 好的实践:在连接条件上使用索引
SELECT * FROM emp e
INNER JOIN dept d ON e.deptno = d.deptno -- deptno字段应该有索引
WHERE e.sal > 2000;
-- 避免在连接条件中使用函数
SELECT * FROM emp e
INNER JOIN dept d ON e.deptno = d.deptno -- 好
-- 而不是:ON YEAR(e.hiredate) = YEAR(d.create_date) -- 不好
7.2 复杂连接查询示例
-- 查询每个部门的员工数量(包括没有员工的部门)
SELECT d.dname, COUNT(e.empno) as employee_count
FROM dept d
LEFT JOIN emp e ON d.deptno = e.deptno
GROUP BY d.deptno, d.dname;
-- 查询没有员工的部门
SELECT d.dname
FROM dept d
LEFT JOIN emp e ON d.deptno = e.deptno
WHERE e.empno IS NULL;
8. LeetCode实战题目
8.1 题目类型分析
-
rank-scores:需要使用自连接或窗口函数进行排名
-
exchange-seats:需要使用CASE语句和自连接进行座位交换
8.2 解题思路示例
-- 类似rank-scores的解法思路
SELECT s1.score, COUNT(DISTINCT s2.score) as 'rank'
FROM scores s1, scores s2
WHERE s1.score <= s2.score
GROUP BY s1.id
ORDER BY s1.score DESC;
9. 总结
9.1 核心要点
-
内连接:求交集,最常用
-
左外连接:保留左表全部记录
-
右外连接:保留右表全部记录
-
实际开发中根据业务需求选择合适的连接方式
9.2 选择指南
-
需要完全匹配的数据 → 使用INNER JOIN
-
需要显示主表所有记录(如:所有学生) → 使用LEFT JOIN
-
需要显示从表所有记录(如:所有成绩) → 使用RIGHT JOIN
-
多表连接时注意连接顺序和性能影响
掌握这些连接方式后,能够处理各种复杂的数据查询需求,是数据库开发的重要技能。