MYSQL LEFT JOIN 详解

概念

JOIN

  • INNER JOIN(内连接,或等值连接): 获取两个表中字段匹配关系的记录。INNER JOIN 中的 INNER 可省略。
  • LEFT JOIN(左连接): 获取左表所有记录,即使右表没有对应匹配的记录。
  • RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。

笛卡尔乘积

定义:笛卡尔乘积是指在数学中,两个集合 X 和 Y 的笛卡尔积(Cartesian product),又称直积,表示为 X×Y,第一个对象是X的成员而第二个对象是Y的所有可能有序对的其中一个成员。
假设集合 A={a, b},集合 B={0, 1, 2},则两个集合的笛卡尔积为 {(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。
来源:百度百科

举例:假设 A 表有 20 条记录,B 表有 30 条记录,则二者关联后的笛卡尔积共 20 * 30 = 600 条记录。也就是说 A 表中的每条记录都会与 B 表的所有记录关联一次,三种关联方式实际上就是对“笛卡尔积”的处理方式不同。

驱动表与被驱动表

  • 当使用 LEFT JOIN 时,左表是驱动表,右表是被驱动表。
  • 当使用 RIGHT JOIN 时,右表是驱动表,左表是被驱动表。
  • 当使用 INNER JOIN 时,mysql 会选择数据量小的表作为驱动表,大表作为被驱动表。

测试

下面我们以 LEFT JOIN 为例,来演示、理解 JOIN 命令。

建表

商品表建表语句如下:

drop table goods;
CREATE TABLE `goods` (
  `goods_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '商品id',
  `goods_name` varchar(128) NOT NULL DEFAULT '' COMMENT '商品名',
  `category_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '分类id',
  PRIMARY KEY (`goods_id`),
  KEY idx_category_id (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';

复制代码商品分类表建表语句如下:

drop table goods_category;
CREATE TABLE `goods_category` (
  `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分类id',
  `category_name` varchar(128) NOT NULL DEFAULT '' COMMENT '分类名',
  PRIMARY KEY (`category_id`),
  KEY idx_category_name (`category_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品分类表';

复制代码数据插入

向商品表插入数据语句如下:

INSERT INTO `goods` (`goods_id`, `goods_name`, `category_id`) VALUES
(1,	'男鞋1',	1),
(2,	'男鞋2',	1),
(3,	'男鞋3',	4),
(4,	'T恤1',	2),
(5,	'T恤2',	2);

复制代码向商品分类表插入数据语句如下:

INSERT INTO `goods_category` (`category_id`, `category_name`) VALUES
(2,	'T恤'),
(3,	'羽绒服'),
(1,	'鞋');

复制代码表数据
最终查看的数据如下:

mysql> select * from goods;
+----------+------------+-------------+
| goods_id | goods_name | category_id |
+----------+------------+-------------+
|        1 | 男鞋1      |           1 |
|        2 | 男鞋2      |           1 |
|        3 | 男鞋3      |           4 |
|        4 | T恤1       |           2 |
|        5 | T恤2       |           2 |
+----------+------------+-------------+
5 rows in set (0.00 sec)
mysql> select * from goods_category;
+-------------+---------------+
| category_id | category_name |
+-------------+---------------+
|           2 | T恤           |
|           3 | 羽绒服        |
|           1 | 鞋            |
+-------------+---------------+
3 rows in set (0.00 sec)

复制代码LEFT JOIN 结果提问
提问,下面语句的执行结果是什么呢?

(1) select * from goods left join goods_category on goods.category_id = goods_category.category_id;

(2) select * from goods left join goods_category on (goods.category_id = goods_category.category_id);

(3) select * from goods g left join goods_category c on (g.category_id = c.category_id and g.goods_name = 'T恤1');

(4) select * from goods g left join goods_category c on (g.category_id = c.category_id and c.category_name = 'T恤');

(5) select * from goods g left join goods_category c on (g.category_id = c.category_id) where c.category_name = '鞋';

(6) select * from goods g left join goods_category c on (g.category_id = c.category_id) where g.goods_name = 'T恤1';

(7) select * from goods g left join goods_category c on (g.category_id = c.category_id and g.goods_name = 'T恤2') where g.goods_name = 'T恤1';

复制代码LEFT JOIN 结果

mysql> select * from goods left join goods_category on goods.category_id = goods_category.category_id;
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        1 | 男鞋1      |           1 |           1 | 鞋            |
|        2 | 男鞋2      |           1 |           1 | 鞋            |
|        3 | 男鞋3      |           3 |           3 | 羽绒服        |
|        4 | T恤1       |           2 |           2 | T恤           |
|        5 | T恤2       |           2 |           2 | T恤           |
+----------+------------+-------------+-------------+---------------+
5 rows in set (0.00 sec)
mysql> select * from goods left join goods_category on goods.category_id = goods_category.category_id;
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        1 | 男鞋1      |           1 |           1 | 鞋            |
|        2 | 男鞋2      |           1 |           1 | 鞋            |
|        3 | 男鞋3      |           4 |        NULL | NULL          |
|        4 | T恤1       |           2 |           2 | T恤           |
|        5 | T恤2       |           2 |           2 | T恤           |
+----------+------------+-------------+-------------+---------------+
5 rows in set (0.00 sec)
mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id and g.goods_name = 'T恤1');
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        1 | 男鞋1      |           1 |        NULL | NULL          |
|        2 | 男鞋2      |           1 |        NULL | NULL          |
|        3 | 男鞋3      |           4 |        NULL | NULL          |
|        4 | T恤1       |           2 |           2 | T恤           |
|        5 | T恤2       |           2 |        NULL | NULL          |
+----------+------------+-------------+-------------+---------------+
5 rows in set (0.00 sec)
mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id and c.category_name = 'T恤');
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        1 | 男鞋1      |           1 |        NULL | NULL          |
|        2 | 男鞋2      |           1 |        NULL | NULL          |
|        3 | 男鞋3      |           4 |        NULL | NULL          |
|        4 | T恤1       |           2 |           2 | T恤           |
|        5 | T恤2       |           2 |           2 | T恤           |
+----------+------------+-------------+-------------+---------------+
5 rows in set (0.00 sec)
mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id) where c.category_name = '鞋';
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        1 | 男鞋1      |           1 |           1 | 鞋            |
|        2 | 男鞋2      |           1 |           1 | 鞋            |
+----------+------------+-------------+-------------+---------------+
2 rows in set (0.00 sec)
mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id) where g.goods_name = 'T恤1';
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        4 | T恤1       |           2 |           2 | T恤           |
+----------+------------+-------------+-------------+---------------+
1 row in set (0.00 sec)
mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id and g.goods_name = 'T恤2') where g.goods_name = 'T恤1';
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        4 | T恤1       |           2 |        NULL | NULL          |
+----------+------------+-------------+-------------+---------------+
1 row in set (0.00 sec)

复制代码从上述结果我们可以发现,同一条件放在 on 结构和 where 结构中,最终的结果是不同的。
那么究竟是什么原因导致的呢,下面我们来分析一下。

on 和 where 条件

在探索 on 和 where 条件有何不同时,我们需要先明确几个概念:

  • LEFT JOIN:LEFT JOIN 会返回左表中的所有行,即使在右表中没有匹配的行。
  • 临时表:数据库在通过连接两张或者多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户。

on 条件分析

我们来单独分析一下 on 条件,以上述三个不同的 on 条件为例:

mysql> select * from goods left join goods_category on goods.category_id = goods_category.category_id;
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        1 | 男鞋1      |           1 |           1 | 鞋            |
|        2 | 男鞋2      |           1 |           1 | 鞋            |
|        3 | 男鞋3      |           4 |        NULL | NULL          |
|        4 | T恤1       |           2 |           2 | T恤           |
|        5 | T恤2       |           2 |           2 | T恤           |
+----------+------------+-------------+-------------+---------------+
5 rows in set (0.00 sec)
mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id and g.goods_name = 'T恤1');
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        1 | 男鞋1      |           1 |        NULL | NULL          |
|        2 | 男鞋2      |           1 |        NULL | NULL          |
|        3 | 男鞋3      |           4 |        NULL | NULL          |
|        4 | T恤1       |           2 |           2 | T恤           |
|        5 | T恤2       |           2 |        NULL | NULL          |
+----------+------------+-------------+-------------+---------------+
5 rows in set (0.00 sec)
mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id and c.category_name = 'T恤');
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        1 | 男鞋1      |           1 |        NULL | NULL          |
|        2 | 男鞋2      |           1 |        NULL | NULL          |
|        3 | 男鞋3      |           4 |        NULL | NULL          |
|        4 | T恤1       |           2 |           2 | T恤           |
|        5 | T恤2       |           2 |           2 | T恤           |
+----------+------------+-------------+-------------+---------------+
5 rows in set (0.00 sec)

无论我们在 on 条件语句中增加 左表的条件 或 右表的条件,还是都不加,最终的结果都是五条数据,即左表的全部数据。
但是右表的数据却不同,具体如下:

  • 左表右表条件都不加:右表返回所有左表关联的数据。
  • 左表增加条件g.goods_name = ‘T恤1’ :右表只返回 与满足该条件的左表数据 关联的数据。
  • 右表增加条件c.category_name = ‘T恤’ :右表只返回满足该条件,并且和左表有关联的数据。

on 条件结论

  • on 条件是生成临时表的条件,所以不管 on 中的条件是否为真,都会返回左表中的所有记录。

  • on 条件会过滤右表数据,无论 on 中的条件是左表的条件还是右表的条件,都是用来过滤右表的数据的。满足条件并且有关联的数据才会写入临时表,不然值为 NULL。

where 条件分析

我们再来分析一下 where 条件对 LEFT JOIN 结果的影响。

mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id) where c.category_name = '鞋';
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        1 | 男鞋1      |           1 |           1 | 鞋            |
|        2 | 男鞋2      |           1 |           1 | 鞋            |
+----------+------------+-------------+-------------+---------------+
2 rows in set (0.00 sec)
mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id) where g.goods_name = 'T恤1';
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        4 | T恤1       |           2 |           2 | T恤           |
+----------+------------+-------------+-------------+---------------+
1 row in set (0.00 sec)
mysql> select * from goods g left join goods_category c on (g.category_id = c.category_id and g.goods_name = 'T恤2') where g.goods_name = 'T恤1';
+----------+------------+-------------+-------------+---------------+
| goods_id | goods_name | category_id | category_id | category_name |
+----------+------------+-------------+-------------+---------------+
|        4 | T恤1       |           2 |        NULL | NULL          |
+----------+------------+-------------+-------------+---------------+
1 row in set (0.00 sec)

从上述结果我们可以看到,在 on 中的条件和在 where 中的条件是不相互影响的。
on (g.goods_name = ‘T恤2’) where g.goods_name = ‘T恤1’ 不会导致最终结果为空的。
同上述只有 on 条件的 SQL 对比来看,where 条件会过滤记录,只留下最后符合 where 条件的记录,条件不为真就全部过滤掉。

where 条件结论

where 条件是在临时表生成好了之后,再对临时表进行过滤的条件。这时已经没有 LEFT JOIN 的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。

总结

on 是在生成临时表的时候使用的条件,不管on的条件是否起到作用,都会返回左表所有的行。
where 则是在生成临时表之后使用的条件,此时已经不管是否使用了 LEFT JOIN 了,只要条件不为真的行,全部过滤掉。
(INNER | LEFT | RIGHT) JOIN 会生成临时表,该临时表为左表,所以我们在写 JOIN 语句的时候应该选择数据量较小的表作为驱动表。

原链接

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Left join是一种在数据库中使用频率较高的连接查询方式。它可以用于获取左表的所有记录,即使右表没有对应匹配的记录。左连接的语法是通过使用LEFT JOIN关键字来实现的。左连接的工作原理是将左表与右表进行连接,并将右表中与左表匹配的记录合并到结果集中。左连接的应用场景包括行记录信息补全、将子查询转换为一个大查询以及将多个case查询转换为一个查询等。在MySQL中,left join可以使用inner joinleft outer join来实现,它们的功能是相同的。因此,left join可以帮助我们在查询中获取到左表的所有记录,而不仅仅是与右表有匹配的记录。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [mysql 左联left join解析及使用指南](https://blog.csdn.net/chuangxin/article/details/86421633)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [MYSQL LEFT JOIN 详解](https://blog.csdn.net/qq_43806430/article/details/127297181)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值