多表查询

1. 多表关系

在设计数据库时,表与表之间常见的关系有三种:

|---------|-----------|--------------------|
| 关系 | 示例 | 实现方式 |
| 一对多 | 部门 → 员工 | 在多的一方建外键 → 一的一方主键 |
| 多对多 | 学生 ↔ 课程 | 建中间表,含两个外键分别指向两表主键 |
| 一对一 | 用户 ↔ 用户详情 | 任一方加外键并设 UNIQUE 约束 |

实战要点 :中间表通常用联合主键 PRIMARY KEY(student_id, course_id)。


2. 内连接(INNER JOIN)

内连接查询两表的交集部分。

2.1 隐式内连接

复制代码
SELECT 字段 FROM 表1, 表2 WHERE 条件;

2.2 显式内连接(推荐)

复制代码
SELECT 字段 FROM 表1 [INNER] JOIN 表2 ON 条件;

为什么推荐显式? 1. 语义更清晰 2. 优化器更容易识别 3. 标准 SQL,跨数据库兼容

复制代码
-- 示例:查询员工姓名及所属部门名称
select e.name, d.name from employee e inner join dept d on e.dept = d.id;

3. 外连接(OUTER JOIN)

3.1 左外连接

查询左表全部数据 + 两表交集。

复制代码
SELECT 字段 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件;

3.2 右外连接

查询右表全部数据 + 两表交集。

复制代码
SELECT 字段 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件;

实践建议:日常开发以左外连接为主,右外连接可改写为左外(交换表顺序即可)。

3.3 内连接 vs 外连接

|-----|------------|----------------|
| 类型 | 返回 | 场景 |
| 内连接 | 两表匹配行 | 查有关联的数据 |
| 外连接 | 主表全部 + 匹配行 | 查"所有"数据(含无关联的) |


4. 自连接

表与自身进行连接。必须使用别名区分。

复制代码
-- 查询员工及其直属领导的姓名
select a.name '员工', b.name '领导'
from employee a left join employee b on a.manager = b.id;

典型场景:组织架构(上级下级)、分类树(父子分类)、评论回复等。


5. 联合查询(UNION)

将多条查询结果合并为一条结果集。

复制代码
SELECT ... FROM 表A
UNION [ALL]
SELECT ... FROM 表B;

|-----------|---------------|
| 关键字 | 说明 |
| UNION | 去重(额外排序,性能略低) |
| UNION ALL | 不去重(性能更高) |

联合查询 vs OR:UNION 不会导致索引失效,OR 一侧无索引则全索引失效。

UNION 使用条件:

  • 多个 SELECT 的列数必须一致
  • 对应列的数据类型必须兼容

6. 子查询

嵌套在 SQL 语句中的 SELECT 语句,外部可以是 INSERT / UPDATE / DELETE / SELECT。

6.1 按结果分类

|-----------|-----------|------------------|----------------|
| 分类 | 返回结果 | 常用操作符 | 示例 |
| 标量子查询 | 单个值(一行一列) | =、<>、>、< | 查销售部所有员工 |
| 列子查询 | 一列多行 | IN、ANY、ALL | 查销售部和市场部员工 |
| 行子查询 | 一行多列 | =、IN | 查与某人薪资和领导相同的员工 |
| 表子查询 | 多行多列 | IN、FROM 后 | 在 FROM 后作为临时表 |

6.2 列子查询操作符详解

|------------|-------------------------|
| 操作符 | 说明 |
| IN | 在集合中 |
| ANY / SOME | 满足任一即可(> ANY → 大于最小值) |
| ALL | 必须满足全部(> ALL → 大于最大值) |

6.3 实战示例

复制代码
-- 标量子查询:查询销售部所有员工
select * from employee where dept = (select id from dept where name='销售部');

-- 列子查询:查询销售部和市场部员工
select * from employee where dept in (select id from dept where name in ('销售部','市场部'));

-- 行子查询:查询与 "张三" 薪资和直属领导相同的员工
select * from employee where (salary, manager) = (select salary, manager from employee where name='张三');

-- 表子查询:查询 2006 年后入职员工及部门(子查询作为临时表)
select e.*, d.* from (select * from employee where entrydate>'2006-01-01') e
left join dept d on e.dept = d.id;
相关推荐
SelectDB3 小时前
SelectDB 是哪个公司的?是国产数据库吗?一篇讲清飞轮科技与 Apache Doris 的关系
数据库
这个DBA有点耶3 小时前
库内计算推理:一条SQL背后的算子融合,和外挂方案差了几次网络往返
数据库·人工智能·sql
这个DBA有点耶5 小时前
函数索引的“完全一致”有多苛刻:写法、字符集、类型,差一处就失效
数据库·mysql·代码规范
星恒随风5 小时前
MySQL 从零入门(三):表的约束详解
linux·服务器·数据库·笔记·mysql
Alluxio6 小时前
Alluxio + OCI:为 AI 工作负载实现亚毫秒访问延迟与可扩展吞吐
运维·数据库·人工智能·分布式·缓存·ai·语言模型
数据库急诊室_阿哲10 小时前
一次加索引建议的复核记录:从报告到签字
数据库·ai数据库
青瓦梦滋11 小时前
Redis主从复制
服务器·数据库·redis·缓存·bootstrap
慧都小妮子11 小时前
Wyn商业智能国产数据库适配:数据接入、分析缓存与审计日志验证清单
数据库·数据分析·数据可视化·审计日志·商业智能
Joy T12 小时前
Redis Lua 原子限流:Lua 脚本、竞态条件与并发控制
数据库·redis·lua·限流·竞态条件·redis scripting
杨凯凡13 小时前
MySQL 死锁怎么排查?InnoDB 锁等待与 Spring Boot 整事务重试实战
数据库·spring boot·mysql