JOIN 后数据为什么重复或减少
从结果粒度、连接键唯一性和未匹配行三个方向定位 JOIN 行数异常。
问题与判断方法
JOIN 的第一步不是选择 INNER 还是 LEFT,而是定义结果粒度:结果中的一行究竟代表客户、订单,还是客户与订单的组合。只有先写清这句话,才能判断连接后的行数是正确展开还是意外放大。
连接键的基数决定重复方式。一对多连接会让一侧记录按匹配数重复,多对多连接会把两侧匹配数相乘;因此客户表与订单表连接后,同一客户出现多行并不一定是错误,但若后续仍按客户金额汇总,就可能重复计算。
结果减少通常来自连接类型或过滤位置。INNER JOIN 会去掉未匹配行;LEFT JOIN 虽然保留左表,但在 WHERE 中筛选右表字段又会去掉 NULL 行。排查时分别统计总行数、COUNT(DISTINCT 业务主键) 和右表主键为 NULL 的数量。
如果目标是一行一个客户,应在连接前把订单聚合到客户粒度,或在连接后明确 GROUP BY 客户字段。若目标是一行一个订单,则应保留订单主键并接受客户信息重复出现。
不要用 DISTINCT 作为默认修复。DISTINCT 只能删除完全相同的结果行,既可能掩盖错误连接,也不能阻止金额等非重复字段被重复汇总。
示例
示例数据表
表名:
customers| id | name |
|---|---|
| 101 | Alice |
| 102 | Bob |
| 103 | Cindy |
表名:
orders| customer_id |
|---|
| 101 |
| 102 |
| 101 |
| 104 |
优化前 / 不推荐
SELECT c.id, c.name FROM customers c JOIN orders o ON c.id = o.customer_id ORDER BY c.id;优化前运行结果
| id | name |
|---|---|
| 101 | Alice |
| 101 | Alice |
| 102 | Bob |
优化后 / 推荐
SELECT c.id, c.name, COUNT(o.id) AS order_count FROM customers c LEFT JOIN orders o ON c.id = o.customer_id GROUP BY c.id, c.name ORDER BY c.id;优化后运行结果
| id | name | order_count |
|---|---|---|
| 101 | Alice | 2 |
| 102 | Bob | 1 |
| 103 | Cindy | 0 |
为什么这样更好:前者的粒度是一行一笔匹配订单,所以 Alice 出现两次且 Cindy 消失;后者明确为一行一个客户,并把订单数聚合到相同粒度。
这篇内容是否清楚?
感谢反馈,我们会把它用于后续内容复核。