You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL LEFT JOIN查询结果与预期不符,求执行原理解析

MySQL LEFT JOIN 实际工作原理解惑

问题背景

我创建了两张数据表tab1和tab2,表结构及数据如下:

CREATE TABLE tab1 (
  id1 int NOT NULL,
  field11 int NOT NULL,
  field12 date NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

INSERT INTO tab1 (id1, field11, field12) VALUES
(1, 11, '2024-07-10'),
(2, 11, '2024-07-10'),
(3, 11, '2024-07-10'),
(4, 12, '2024-07-11'),
(5, 12, '2024-07-11'),
(6, 12, '2024-07-11');

CREATE TABLE tab2 (
  id2 int NOT NULL,
  field21 int NOT NULL,
  field22 date NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

INSERT INTO tab2 (id2, field21, field22) VALUES
(1, 11, '2024-07-01'),
(2, 11, '2024-07-01'),
(3, 11, '2024-07-01'),
(4, 22, '2024-07-02'),
(5, 22, '2024-07-02'),
(6, 22, '2024-07-02');

执行以下LEFT JOIN查询语句:

select * 
from tab1 t1
left join tab2 t2 on t1.field11 = t2.field21;

得到的实际结果为:

+-----+---------+------------+------+---------+------------+
| id1 | field11 | field12    | id2  | field21 | field22    |
+-----+---------+------------+------+---------+------------+
|   1 |      11 | 2024-07-10 |    3 |      11 | 2024-07-01 |
|   1 |      11 | 2024-07-10 |    2 |      11 | 2024-07-01 |
|   1 |      11 | 2024-07-10 |    1 |      11 | 2024-07-01 |
|   2 |      11 | 2024-07-10 |    3 |      11 | 2024-07-01 |
|   2 |      11 | 2024-07-10 |    2 |      11 | 2024-07-01 |
|   2 |      11 | 2024-07-10 |    1 |      11 | 2024-07-01 |
|   3 |      11 | 2024-07-10 |    3 |      11 | 2024-07-01 |
|   3 |      11 | 2024-07-10 |    2 |      11 | 2024-07-01 |
|   3 |      11 | 2024-07-10 |    1 |      11 | 2024-07-01 |
|   4 |      12 | 2024-07-11 | NULL |    NULL | NULL       |
|   5 |      12 | 2024-07-11 | NULL |    NULL | NULL       |
|   6 |      12 | 2024-07-11 | NULL |    NULL | NULL       |
+-----+---------+------------+------+---------+------------+
12 rows in set (0.00 sec)

但我预期的结果是:

+-----+---------+------------+------+---------+------------+
| id1 | field11 | field12    | id2  | field21 | field22    |
+-----+---------+------------+------+---------+------------+
|   1 |      11 | 2024-07-10 |    1 |      11 | 2024-07-01 |
|   2 |      11 | 2024-07-10 |    2 |      11 | 2024-07-01 |
|   3 |      11 | 2024-07-10 |    3 |      11 | 2024-07-01 |
|   4 |      12 | 2024-07-11 | NULL |    NULL | NULL       |
|   5 |      12 | 2024-07-11 | NULL |    NULL | NULL       |
|   6 |      12 | 2024-07-11 | NULL |    NULL | NULL       |
+-----+---------+------------+------+---------+------------+

我的预期基于W3Schools的文档说明:

LEFT JOIN关键字返回左表(table1)的所有记录,以及右表(table2)中匹配的记录(如果有)。

同时参考左连接图示,认为结果应仅对应图中的绿色区域。请问MySQL的LEFT JOIN实际是如何工作的?

核心解答

MySQL LEFT JOIN 的实际逻辑

LEFT JOIN的核心规则是:左表中的每一行,都会与右表中所有满足ON条件的行进行关联,生成新的结果行。如果左表的某一行在右表中没有匹配的行,就用NULL填充右表的字段。

回到你的案例:

  • tab1中field11=11的行有3条(id1=1、2、3)
  • tab2中field21=11的行有3条(id2=1、2、3)
  • 每一条左表的field11=11的行,都会和右表的3条匹配行分别关联,所以生成3×3=9条结果行
  • tab1中field11=12的3条行,在tab2中没有匹配的field21=12的行,所以每条行对应一条右表字段全为NULL的结果行,共3条
  • 最终总结果就是9+3=12条,这是符合LEFT JOIN标准逻辑的正确结果

对你误解的澄清

W3Schools的描述本身没错,但你对“右表中匹配的记录”理解有误——这里的“匹配的记录”指的是所有满足条件的记录,而不是仅一条。左连接图示的绿色区域确实代表左表所有行加上匹配的右表行,但这里的“匹配”是左表每行对应所有符合条件的右表行,而非一对一的匹配。

如何得到你预期的结果

如果你希望左表的每行仅关联右表中field21匹配的某一条记录(比如每个field21组的第一条),可以通过子查询或窗口函数实现:

方法1:子查询取每组最小id的记录

select t1.*, t2.id2, t2.field21, t2.field22
from tab1 t1
left join (
    select * from tab2 where id2 in (select min(id2) from tab2 group by field21)
) t2 on t1.field11 = t2.field21;

方法2:窗口函数分组取第一条

select t1.*, t2.id2, t2.field21, t2.field22
from tab1 t1
left join (
    select *, ROW_NUMBER() over (partition by field21 order by id2) as rn 
    from tab2
) t2 on t1.field11 = t2.field21 and t2.rn = 1;

这两种方法都能让左表每行仅关联右表中对应field21的一条记录,得到你预期的6行结果。

内容的提问来源于stack exchange,提问作者Dennosaur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:29:53