多表连接是什么
把三张以上的表依次连接取数,是报表与业务查询的常态。每一步连接都可能改变行数与粒度:一对多会放大行数,内连接会丢掉未匹配行。动手前先明确“最终一行代表什么”,再决定连接类型与条件摆放的位置。
基本信息
| 书写顺序 | 按依赖链式书写:主表连接明细表,再连接各维表 |
|---|---|
| 结果粒度 | 一行等于最细粒度表的一行;订单连明细后订单会重复出现 |
| 连接类型 | 按保留侧选 INNER 或 LEFT,一段一段确认,不要整句混用 |
| 条件位置 | 过滤主表条件放 WHERE,过滤被连接表的条件放 ON 才不改变外连接语义 |
| 核对方法 | 分别统计各表过滤后的行数,再与最终结果行数比对 |
| 汇总口径 | 明细放大后仍要数订单,用 COUNT(DISTINCT o.id) |
三表连接示例
三表连接示例
SELECT o.id, u.name, i.product_name, i.qty
FROM orders o
JOIN users u ON u.id = o.user_id -- 多对一:用户键唯一则不放大行数
JOIN order_items i ON i.order_id = o.id -- 一对多:按明细条数放大行数
WHERE o.status = 1 -- 主表过滤条件放 WHERE
ORDER BY o.id, i.id
LIMIT 100; -- 分页要保证排序键唯一,避免翻页重复每一步的行数变化
| 连接步骤 | 关系 | 行数影响 |
|---|---|---|
| orders 连 users | 多对一 | 不放大,前提是 users 主键唯一 |
| orders 连 order_items | 一对多 | 按明细条数放大,一个订单可能多行 |
| orders 左连 payment | 一对一或一对多 | 未支付订单保留,支付列为 NULL |
| 再连 products 维表 | 多对一 | 一般不放大,先确认维表键唯一 |
| LEFT 与 INNER 混用 | — | 后面的 INNER 会把前面补出的 NULL 行过滤掉 |
典型使用场景
- 订单主表串联明细与用户信息,产出明细报表
- 补齐多张维表的描述字段:用户、商品、渠道名称
- 日志与主体表关联做行为分析,例如订单与支付流水对照
- 先连接明细再按主表键分组,算出每单金额与件数
- 数据核对:把两份来源的数据连接起来,找出只在一侧存在的记录
- 列表分页查询:主表连维表,排序键唯一才能保证分页不重不漏
链式 JOIN 是依次传递的:后面的 JOIN 作用在前面的结果集上。所以条件写在 ON 还是 WHERE、前一段用 LEFT 还是 INNER,都会影响后面每一步能看到的行。
混用外连接与内连接时,后面写的 INNER JOIN 会把前面补出的 NULL 行重新过滤掉,看起来“外连接失效”。要么统一连接类型,要么把后续条件放进 ON,并把 NULL 参与比较的情况单独处理。