SQL 连接查询用法总结
核心:多表通过关联字段拼接数据,分为内连接、左连接、右连接、全连接、交叉连接
一、基础语法通用格式
sql
SELECT 字段
FROM 表1
连接类型 JOIN 表2
ON 表1.关联字段 = 表2.关联字段;
二、五类连接详解
1. 内连接 INNER JOIN
只返回两表匹配成功的数据,交集部分
sql
SELECT *
FROM user u
INNER JOIN order o
ON u.id = o.user_id;
简写:可省略INNER,直接写JOIN
2. 左连接 LEFT JOIN
以左表为基准,左表数据全部保留,右表匹配不上显示null
sql
-- 查询所有用户及对应订单,无订单也保留用户
SELECT *
FROM user u
LEFT JOIN order o
ON u.id = o.user_id;
3. 右连接 RIGHT JOIN
以右表为基准,右表数据全部保留,左表匹配不上显示null
sql
SELECT *
FROM user u
RIGHT JOIN order o
ON u.id = o.user_id;
4. 全连接 FULL JOIN
取两表所有数据,匹配不上字段补空
sql
SELECT *
FROM user u
FULL JOIN order o
ON u.id = o.user_id;
注:MySQL不原生支持,可用左右连接合并实现
5. 交叉连接 CROSS JOIN
笛卡尔积,两表行数相乘组合,极少主动使用
sql
SELECT * FROM user CROSS JOIN goods;
三、关键区分速记
| 连接类型 | 数据范围 | 使用场景 |
|---|---|---|
| INNER JOIN | 两边匹配数据 | 只查有关联的有效数据 |
| LEFT JOIN | 左表全部+匹配右表 | 主表数据必展示,附属信息可选 |
| RIGHT JOIN | 右表全部+匹配左表 | 极少用,可互换表改用左连接 |
| FULL JOIN | 两表全部数据 | 汇总全部台账数据 |
四、常用拓展写法
- 多表连续连接
sql
SELECT *
FROM user u
LEFT JOIN order o ON u.id=o.user_id
LEFT JOIN pay p ON o.id=p.order_id;
- 连接后加筛选条件
ON:连接前过滤关联规则WHERE:连接结果后整体筛选
sql
SELECT *
FROM user u
LEFT JOIN order o ON u.id=o.user_id
WHERE o.create_time>'2025-01-01';
五、实操要点
- 关联字段一般为主键、外键,保证对应关系
- 字段重名需加表别名区分
- 优先左连接,可读性更高,日常业务最常用
- 内连接自动过滤无效无关联数据
六、例题
学生表: Students
| Column Name | Type |
|---|---|
| student_id | int |
| student_name | varchar |
在 SQL 中,主键为 student_id(学生ID)。
该表内的每一行都记录有学校一名学生的信息。
科目表: Subjects
| Column Name | Type |
|---|---|
| subject_name | varchar |
| 在 SQL 中,主键为 subject_name(科目名称)。 | |
| 每一行记录学校的一门科目名称。 |
考试表: Examinations
| Column Name | Type |
|---|---|
| student_id | int |
| subject_name | varchar |
这个表可能包含重复数据(换句话说,在 SQL 中,这个表没有主键)。
学生表里的一个学生修读科目表里的每一门科目。
这张考试表的每一行记录就表示学生表里的某个学生参加了一次科目表里某门科目的测试。
查询出每个学生参加每一门科目测试的次数,结果按 student_id 和 subject_name 排序。
查询结构格式如下所示。
示例 1:
输入:
Students table:
| student_id | student_name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 13 | John |
| 6 | Alex |
Subjects table:
| subject_name |
|---|
| Math |
| Physics |
| Programming |
Examinations table:
| student_id | subject_name |
|---|---|
| 1 | Math |
| 1 | Physics |
| 1 | Programming |
| 2 | Programming |
| 1 | Physics |
| 1 | Math |
| 13 | Math |
| 13 | Programming |
| 13 | Physics |
| 2 | Math |
| 1 | Math |
输出:
| student_id | student_name | subject_name | attended_exams |
|---|---|---|---|
| 1 | Alice | Math | 3 |
| 1 | Alice | Physics | 2 |
| 1 | Alice | Programming | 1 |
| 2 | Bob | Math | 1 |
| 2 | Bob | Physics | 0 |
| 2 | Bob | Programming | 1 |
| 6 | Alex | Math | 0 |
| 6 | Alex | Physics | 0 |
| 6 | Alex | Programming | 0 |
| 13 | John | Math | 1 |
| 13 | John | Physics | 1 |
| 13 | John | Programming | 1 |
解释:
结果表需包含所有学生和所有科目(即便测试次数为0):
Alice 参加了 3 次数学测试, 2 次物理测试,以及 1 次编程测试;
Bob 参加了 1 次数学测试, 1 次编程测试,没有参加物理测试;
Alex 啥测试都没参加;
John 参加了数学、物理、编程测试各 1 次。
题目分析 + 左外连接完整解法
这道题是经典的多表左连接 + 分组统计 考题,核心难点在于:
必须让每个学生 × 每门科目都出现一行,哪怕一次考试都没参加
解题思路
- 生成「所有学生 × 所有科目」的完整组合
- 学生表(所有学生)
- 科目表(所有科目)
- 用 笛卡尔积(CROSS JOIN) 生成:每个学生对应每一门科目
→ 这是保证「不漏学生、不漏科目」的关键
- 左连接考试表,统计参加次数
- 用 LEFT JOIN 把考试表关联上去
- 没参加考试的次数记为 0
- 分组 + 排序
- 按
student_id, subject_name分组统计次数 - 最后按要求排序
最终 SQL 代码
sql
SELECT
s.student_id,
s.student_name,
sub.subject_name,
COUNT(e.student_id) AS attended_exams -- 统计考试次数
FROM
Students s
CROSS JOIN
Subjects sub -- 生成 每个学生 × 每门科目
LEFT JOIN
Examinations e -- 左连接考试表(不丢组合)
ON s.student_id = e.student_id
AND sub.subject_name = e.subject_name
GROUP BY
s.student_id, s.student_name, sub.subject_name
ORDER BY
s.student_id, sub.subject_name;
关键步骤解释
-
CROSS JOIN 作用
Students CROSS JOIN Subjects直接生成:所有学生 × 所有科目 的完整组合
哪怕学生没考过某科目,这一行依然存在
-
LEFT JOIN 为什么必须这么写
sql
LEFT JOIN Examinations e
ON s.student_id = e.student_id
AND sub.subject_name = e.subject_name
- 以「学生+科目」为主表
- 左边完整保留
- 右边匹配不到考试记录 → 显示 NULL
COUNT(e.student_id)会自动把 NULL 记为 0
- 统计次数
COUNT(e.student_id)
- 参加过 → 计数 +1
- 没参加 → 计 0
为什么不能直接用考试表?
因为考试表里缺少没参加考试的学生/科目组合
直接统计会漏数据,必须先生成完整组合,再左连接考试表。