多表联查是关系型数据库的核心操作,用于从多个表中关联数据。MySQL 支持多种连接方式,最常用的是内连接和外连接(左/右/全外连接)。
一、多表联查基础语法
SELECT 列列表
FROM 表1
[连接类型] JOIN 表2 ON 连接条件
[连接类型] JOIN 表3 ON 连接条件
...
WHERE 筛选条件;
类型 | 行数公式 | 需要 ON/WHERE 条件 | 典型场景 |
---|---|---|---|
CROSS JOIN | M × N | 不需要 | 生成组合、枚举 |
INNER JOIN | ≤ M × N | 需要 | 等值关联 |
LEFT JOIN | ≥ M | 需要 | 保留左表全部行 |
RIGHT JOIN | ≥ N | 需要 | 保留右表全部行 |
FULL JOIN | ≥ max(M,N) | 需要 | 保留两边全部行 |
``
select 列名 from 表1名 a xxx join 表2名 b on a.xx=b.xx;# 对应的条件在on后填写
A inner join B:取交集(交集是选择列名和on后的条件组成)
A left join B:取A全部,B没有对应的值,则为null
A right join B:取B全部,A没有对应的值,则为null
A full outer join B:取并集,彼此没有对应的值为null(需用union实现)
注:内外连接也可参考MySQL增删改查中的第9点
二、连接类型详解
1. 内连接 (INNER JOIN)
特点:只返回两个表中匹配成功的记录
应用场景:需要获取有关联关系的完整数据
-- 基础语法
SELECT
e.name AS 员工姓名,
d.name AS 部门名称
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id;
-- 三表内连接示例
SELECT
o.order_id,
c.name AS 客户名称,
p.product_name AS 产品名称
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id
INNER JOIN order_items oi ON o.id = oi.order_id
INNER JOIN products p ON oi.product_id = p.id;
2. 左外连接 (LEFT JOIN)
特点:返回左表所有记录 + 右表匹配记录(无匹配则显示 NULL)
应用场景:包含主表全部记录,关联表可选信息
-- 获取所有员工及其部门(含无部门员工)
SELECT
e.name AS 员工,
d.name AS 部门
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id;
-- 查找从未下单的客户
SELECT
c.name AS 客户名
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.id IS NULL; -- 关键:通过NULL检测未匹配项
3. 右外连接 (RIGHT JOIN)
特点:返回右表所有记录 + 左表匹配记录(无匹配则显示 NULL)
应用场景:包含从表全部记录,主表可选信息(较少使用,可用LEFT JOIN替代)
-- 获取所有部门及员工(含无员工部门)
SELECT
d.name AS 部门,
e.name AS 员工
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.id;
-- 等价LEFT JOIN写法
SELECT
d.name AS 部门,
e.name AS 员工
FROM departments d
LEFT JOIN employees e ON d.id = e.dept_id;
4. 全外连接 (FULL OUTER JOIN)
特点:返回左右表所有记录(无匹配则对侧显示 NULL)
注意:MySQL 不直接支持,需用 UNION
实现
-- 获取所有员工和部门组合(含无部门员工+无员工部门)
SELECT
e.name AS 员工,
d.name AS 部门
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id
UNION -- 使用UNION合并结果集
SELECT
e.name AS 员工,
d.name AS 部门
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.id
WHERE e.dept_id IS NULL; -- 排除重复匹配项
5. 交叉连接 (CROSS JOIN)
特点:返回笛卡尔积(所有可能组合)
应用场景:生成组合数据(如测试数据)
-- 生成颜色和尺寸的所有组合
SELECT
colors.color_name,
sizes.size_name
FROM colors
CROSS JOIN sizes;
三、特殊连接场景
1. 自连接 (Self Join)
应用场景:表内数据关联(如层级关系)
-- 查询员工及其经理
SELECT
emp.name AS 员工,
mgr.name AS 经理
FROM employees emp
LEFT JOIN employees mgr ON emp.manager_id = mgr.id;
2. 复合条件连接
-- 多条件连接(部门+地点)
SELECT
e.name,
d.name AS 部门,
loc.city AS 城市
FROM employees e
INNER JOIN departments d
ON e.dept_id = d.id
AND d.location_id = loc.id -- 连接时添加额外条件
INNER JOIN locations loc ON d.location_id = loc.id;
3. USING 关键字
适用:当连接列名相同时简化语法
-- 传统写法
SELECT *
FROM orders o
INNER JOIN order_items i ON o.id = i.order_id;
-- 使用USING简化
SELECT *
FROM orders
INNER JOIN order_items USING (id); -- 要求两表都有id列
四、性能优化与最佳实践
-
索引策略
-- 为连接字段创建索引 CREATE INDEX idx_dept ON employees(dept_id); CREATE INDEX idx_order ON order_items(order_id);
-
**避免 SELECT ***
只选择必要字段减少数据传输量 -
连接顺序优化
- 小表驱动大表(小表在前)
- 过滤条件多的表优先连接
-
使用 EXPLAIN 分析
EXPLAIN SELECT ...
查看执行计划,优化连接顺序和索引使用
-
替代方案考虑
- 复杂连接可拆分为多个查询
- 大表连接考虑使用临时表
五、综合应用示例
-- 查询2023年每个客户的总消费金额(含未消费客户)
SELECT
c.id AS 客户ID,
c.name AS 客户姓名,
COALESCE(SUM(o.amount), 0) AS 总消费金额
FROM customers c
LEFT JOIN orders o
ON c.id = o.customer_id
AND YEAR(o.order_date) = 2023 -- 连接时过滤
GROUP BY c.id
ORDER BY 总消费金额 DESC;
-- 结果示例:
| 客户ID | 客户姓名 | 总消费金额 |
|---------|----------|------------|
| 101 | 张三 | 8500.00 |
| 105 | 李四 | 0.00 |
| 102 | 王五 | 4200.00 |
六、常见错误及解决
-
笛卡尔积问题
现象:结果集异常膨胀
解决:确保所有表都有连接条件 -
NULL 值匹配问题
现象:预期外的记录缺失
解决:使用IFNULL()
或COALESCE()
处理 -
性能低下
现象:大表连接缓慢
解决:-- 添加合适索引 CREATE INDEX idx_name ON table(column); -- 分批处理 SELECT ... LIMIT 1000 OFFSET 0;