深入理解 SQL 的 7 种 JOIN 操作
在 SQL 中,JOIN 是一个非常重要的操作,它用于连接两个或多个表,从而根据条件返回相关数据。不同类型的 JOIN 有不同的连接方式,根据实际需求可以返回不同的数据集。在本文中,我们将通过 学生表(Students) 和 成绩表(Scores) 的例子,深入了解七种常见的 JOIN 操作,并通过 员工表(Employees) 和 部门表(Departments) 的例子讲解 SELF JOIN 和 NATURAL JOIN。
学生表(Students)
| student_id | 
name | 
department_id | 
| 1 | 
Alice | 
10 | 
| 2 | 
Bob | 
20 | 
| 3 | 
Charlie | 
10 | 
| 4 | 
David | 
NULL | 
成绩表(Scores)
| student_id | 
score | 
| 1 | 
85 | 
| 2 | 
90 | 
| 3 | 
75 | 
| 5 | 
80 | 
1. INNER JOIN
INNER JOIN 返回两个表中 有匹配的行。如果在一个表中没有匹配的记录,那么这条记录将不会出现在结果中。
SQL 查询
1 2 3 4
   | SELECT *  FROM students INNER JOIN scores ON students.student_id = scores.student_id;
   | 
 
结果
| student_id | 
name | 
department_id | 
student_id | 
score | 
| 1 | 
Alice | 
10 | 
1 | 
85 | 
| 2 | 
Bob | 
20 | 
2 | 
90 | 
| 3 | 
Charlie | 
10 | 
3 | 
75 | 
- Alice 和 Charlie 的 
student_id 都在成绩表中有记录,匹配到成绩。Bob 也有成绩记录。 
- David 没有成绩记录,所以没有出现在结果中。
 
2. LEFT JOIN (LEFT OUTER JOIN)
LEFT JOIN 返回 左表(students) 中的所有行,以及右表(成绩表)中与之匹配的行。如果右表没有匹配的行,右表的列会显示 NULL。
SQL 查询
1 2 3 4
   | SELECT *  FROM students LEFT JOIN scores ON students.student_id = scores.student_id;
   | 
 
结果
| student_id | 
name | 
department_id | 
student_id | 
score | 
| 1 | 
Alice | 
10 | 
1 | 
85 | 
| 2 | 
Bob | 
20 | 
2 | 
90 | 
| 3 | 
Charlie | 
10 | 
3 | 
75 | 
| 4 | 
David | 
NULL | 
NULL | 
NULL | 
- David 没有成绩记录,因此他的成绩为 
NULL。 
- 其他学生的成绩都正常返回。
 
3. RIGHT JOIN (RIGHT OUTER JOIN)
RIGHT JOIN 与 LEFT JOIN 相似,不过它更关注右表(成绩表),返回右表中的所有行,即使左表没有匹配的行。如果左表没有匹配的行,左表的列会显示 NULL。
SQL 查询
1 2 3 4
   | SELECT *  FROM students RIGHT JOIN scores ON students.student_id = scores.student_id;
   | 
 
结果
| student_id | 
name | 
department_id | 
student_id | 
score | 
| 1 | 
Alice | 
10 | 
1 | 
85 | 
| 2 | 
Bob | 
20 | 
2 | 
90 | 
| 3 | 
Charlie | 
10 | 
3 | 
75 | 
| NULL | 
NULL | 
NULL | 
5 | 
80 | 
- 结果中包含了右表(成绩表)中的所有记录。
 
- NULL 行显示的是成绩表中的一条记录(
student_id = 5),但在学生表中没有对应记录。 
4. FULL JOIN (FULL OUTER JOIN)
FULL JOIN 返回 左表 和 右表 中的所有行,即使某一表没有匹配的行。如果没有匹配的行,缺少的部分会显示 NULL。
SQL 查询
1 2 3 4
   | SELECT *  FROM students FULL JOIN scores ON students.student_id = scores.student_id;
   | 
 
结果
| student_id | 
name | 
department_id | 
student_id | 
score | 
| 1 | 
Alice | 
10 | 
1 | 
85 | 
| 2 | 
Bob | 
20 | 
2 | 
90 | 
| 3 | 
Charlie | 
10 | 
3 | 
75 | 
| 4 | 
David | 
NULL | 
NULL | 
NULL | 
| NULL | 
NULL | 
NULL | 
5 | 
80 | 
- David 没有成绩记录,因此他的成绩为 
NULL。 
- 成绩表中 
student_id = 5 的记录没有对应的学生,学生名为 NULL。 
5. CROSS JOIN
CROSS JOIN 返回 左表 和 右表 的 笛卡尔积,即每一行左表与每一行右表的所有可能组合。它没有连接条件,所有行都会组合。
SQL 查询
1 2 3
   | SELECT *  FROM students CROSS JOIN scores;
   | 
 
结果
| student_id | 
name | 
department_id | 
student_id | 
score | 
| 1 | 
Alice | 
10 | 
1 | 
85 | 
| 1 | 
Alice | 
10 | 
2 | 
90 | 
| 1 | 
Alice | 
10 | 
3 | 
75 | 
| 1 | 
Alice | 
10 | 
5 | 
80 | 
| 2 | 
Bob | 
20 | 
1 | 
85 | 
| 2 | 
Bob | 
20 | 
2 | 
90 | 
| 2 | 
Bob | 
20 | 
3 | 
75 | 
| 2 | 
Bob | 
20 | 
5 | 
80 | 
| 3 | 
Charlie | 
10 | 
1 | 
85 | 
| 3 | 
Charlie | 
10 | 
2 | 
90 | 
| 3 | 
Charlie | 
10 | 
3 | 
75 | 
| 3 | 
Charlie | 
10 | 
5 | 
80 | 
| 4 | 
David | 
NULL | 
1 | 
85 | 
| 4 | 
David | 
NULL | 
2 | 
90 | 
| 4 | 
David | 
NULL | 
3 | 
75 | 
| 4 | 
David | 
NULL | 
5 | 
80 | 
- 结果显示了左表(学生表)与右表(成绩表)的每一行组合。
 
- 总共生成了 4 个学生 × 4 个成绩 = 16 条记录。
 
6. SELF JOIN
SELF JOIN 是对同一个表进行连接。我们通过为同一表指定不同的别名,来实现表与自身之间的连接。SELF JOIN 适用于查找表中记录之间的关系,例如员工与经理的关系。
假设我们有一个员工表(Employees):
| emp_id | 
name | 
manager_id | 
| 1 | 
Alice | 
3 | 
| 2 | 
Bob | 
3 | 
| 3 | 
Charlie | 
NULL | 
| 4 | 
David | 
2 | 
我们想要查询每个员工的姓名及其经理的姓名。
SQL 查询
1 2 3 4
   | SELECT *  FROM employees e1 LEFT JOIN employees e2 ON e1.manager_id = e2.emp_id;
   | 
 
结果
| emp_id | 
name | 
manager_id | 
emp_id | 
name | 
manager_id | 
| 1 | 
Alice | 
3 | 
3 | 
Charlie | 
NULL | 
| 2 | 
Bob | 
3 | 
3 | 
Charlie | 
NULL | 
| 3 | 
Charlie | 
NULL | 
NULL | 
NULL | 
NULL | 
| 4 | 
David | 
2 | 
2 | 
Bob | 
3 | 
- Alice 和 Bob 的经理是 Charlie,而 David 的经理是 Bob。
 
7. NATURAL JOIN
NATURAL JOIN 自动根据两个表中 列名相同的列 进行连接。你不需要显式指定连接条件,它会自动查找并使用同名的列。
假设我们有一个员工表(Employees)和部门表(Departments):
员工表(Employees):
| emp_id | 
name | 
department_id | 
| 1 | 
Alice | 
10 | 
| 2 | 
Bob | 
20 | 
| 3 | 
Charlie | 
10 | 
| 4 | 
David | 
NULL | 
部门表(Departments):
| department_id | 
department_name | 
| 10 | 
HR | 
| 20 | 
IT | 
我们希望查询每个员工的姓名及其部门名称。
SQL 查询
1 2 3
   | SELECT *  FROM employees NATURAL JOIN departments;
   | 
 
结果
| emp_id | 
name | 
department_id | 
department_name | 
| 1 | 
Alice | 
10 | 
HR | 
| 2 | 
Bob | 
20 | 
IT | 
| 3 | 
Charlie | 
10 | 
HR | 
NATURAL JOIN 自动使用 department_id 列进行连接。 
- 结果中不再重复显示 
department_id 列,因为它已经被自动连接并去除。 
总结归纳
SQL 提供了多种 JOIN 操作方式,每种方式根据不同的需求连接表格数据。通过我们在 学生表(Students) 和 成绩表(Scores) 的例子,你可以清晰地了解每种 JOIN 类型的特点和用法。
- INNER JOIN:返回匹配的记录。
 
- LEFT JOIN:返回左表中的所有记录,右表没有匹配的返回 
NULL。 
- RIGHT JOIN:返回右表中的所有记录,左表没有匹配的返回 
NULL。 
- FULL JOIN:返回左表和右表中的所有记录,缺少匹配的返回 
NULL。 
- CROSS JOIN:返回左表和右表的笛卡尔积,所有可能的组合。
 
- SELF JOIN:一个表与自身进行连接,通常用于查找表中记录之间的关系。
 
- NATURAL JOIN:根据列名相同的列自动连接表。