你认为自己已对 MySQL 的 LEFT JOIN 理解深刻,这篇文章,我想让你能多学会点东西!

  • ON 子句与 WHERE 子句的不同
  • 一种更好地理解带有 WHERE ... IS NULL 子句的复杂匹配条件的简单方法
  • Matching-Conditions 与 Where-conditions 的不同

你一定知道关于 “A LEFT JOIN B ON 条件表达式” 的基础用法

ON 条件(“A LEFT JOIN B ON 条件表达式”中的ON)用来决定如何从 B 表中检索数据行。

如果 B 表中没有任何一行数据匹配 ON 的条件,将会额外生成一行所有列为 NULL 的数据

在匹配阶段 WHERE 子句的条件都不会被使用。仅在匹配阶段完成以后,WHERE 子句条件才会被使用。它将从匹配阶段产生的数据中检索过滤。

例如:news 与 news_category表的结构如下,news表的category_id与news_category表的id是对应关系。

显示news表记录,并显示news的category名称,查询语句如下

select a.id,a.title,b.name as category_name,a.content,a.addtime,a.lastmodify
from news as a left join news_category as b
on a.category_id = b.id;

因 news_category 表没有id=4的记录,因此news 表中category_id=4的记录的category_name=NULL

使用left join, A表与B表所显示的记录数为 1:1 或 1:0,A表的所有记录都会显示,B表只显示符合条件的记录。

但如果B表符合条件的记录数大于1条,就会出现1:n的情况,这样left join后的结果,记录数会多于A表的记录数。

例如:member与member_login_log表的结构如下,member记录会员信息,member_login_log记录会员每日的登入记录。member表的id与member_login_log表的uid是对应关系。

查询member用户的资料及最后登入日期:

如果直接使用left join

select a.id, a.username, b.logindate
from member as a
left join member_login_log as b on a.id = b.uid;

保证B表的符合条件的记录是空或唯一,我们可以使用group by来实现。

select a.id, a.username, b.logindate
from member as a
left join (select uid, max(logindate) as logindate from member_login_log group by uid) as b
on a.id = b.uid;

小结:使用left join的两个表,最好是1:1 或 1:0的关系,这样可以保证A表的记录全部显示,B表显示符合条件的记录。

创建表及测试数据

mysql> CREATE TABLE `product` (
`id` int(10) unsigned NOT NULL auto_increment,
`amount` int(10) unsigned default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1 mysql> CREATE TABLE `product_details` (
`id` int(10) unsigned NOT NULL,
`weight` int(10) unsigned default NULL,
`exist` int(10) unsigned default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 mysql> INSERT INTO product (id,amount)
VALUES (1,100),(2,200),(3,300),(4,400);
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0 mysql> INSERT INTO product_details (id,weight,exist)
VALUES (2,22,0),(4,44,1),(5,55,0),(6,66,1);
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM product;
+----+--------+
| id | amount |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
| 4 | 400 |
+----+--------+
4 rows in set (0.00 sec) mysql> SELECT * FROM product_details;
+----+--------+-------+
| id | weight | exist |
+----+--------+-------+
| 2 | 22 | 0 |
| 4 | 44 | 1 |
| 5 | 55 | 0 |
| 6 | 66 | 1 |
+----+--------+-------+
4 rows in set (0.00 sec) mysql> SELECT * FROM product LEFT JOIN product_details
ON (product.id = product_details.id);
+----+--------+------+--------+-------+
| id | amount | id | weight | exist |
+----+--------+------+--------+-------+
| 1 | 100 | NULL | NULL | NULL |
| 2 | 200 | 2 | 22 | 0 |
| 3 | 300 | NULL | NULL | NULL |
| 4 | 400 | 4 | 44 | 1 |
+----+--------+------+--------+-------+
4 rows in set (0.00 sec)

ON 子句和 WHERE 子句有什么不同?

一个问题:下面两个查询的结果集有什么不同么?

1. SELECT
*
FROM
product
LEFT JOIN product_details ON (
product.id = product_details.id
)
AND product_details.id = 2;
2. SELECT
*
FROM
product
LEFT JOIN product_details ON (
product.id = product_details.id
)
WHERE
product_details.id = 2;

第一条查询使用 ON 条件决定了从 LEFT JOIN的 product_details表中检索符合的所有数据行。

第二条查询做了简单的LEFT JOIN,然后使用 WHERE 子句从 LEFT JOIN的数据中过滤掉不符合条件的数据行。

再看一个示例:

SELECT
*
FROM
product
LEFT JOIN product_details ON product.id = product_details.id
AND product.amount = 100;

有来自product表的数据行都被检索到了,但没有在product_details表中匹配到记录(product.id = product_details.id AND product.amount=100 条件并没有匹配到任何数据)

SELECT * FROM product LEFT JOIN product_details
ON (product.id = product_details.id)
AND product.amount=200;

同样,所有来自product表的数据行都被检索到了,有一条数据匹配到了。

使用 WHERE ... IS NULL 子句的 LEFT JOIN 会发生什么呢?

WHERE 条件查询发生在 匹配阶段之后,这意味着 WHERE ... IS NULL 子句将从匹配阶段后的数据中过滤掉不满足匹配条件的数据行。

纸面上看起来很清楚,但是当你在 ON 子句中使用多个条件时就会感到困惑了。

我总结了一种简单的方式来理解上述情况:

  • 将 IS NULL 作为否定匹配条件
  • 使用 !(A and B) == !A OR !B 逻辑判断 看看下面的示例:
SELECT a.* FROM product a LEFT JOIN product_details b
ON a.id=b.id AND b.weight!=44 AND b.exist=0
WHERE b.id IS NULL;

检查一下 ON 匹配子句:我们可以把 IS NULL 子句 看作是否定匹配条件。

这意味着我们将检索到以下行:

!( exist(b.id that equals to a.id) AND b.weight !=44 AND b.exist=0 )
!exist(b.id that equals to a.id) || !(b.weight !=44) || !(b.exist=0)
!exist(b.id that equals to a.id) || b.weight =44 || b.exist=1

就像在C语言中的逻辑 AND 和 逻辑 OR表达式一样,其操作数是从左到右求值的。如果第一个参数做够判断操作结果,那么第二个参数便不会被计算求值(短路效果)

看看别的示例:

SELECT a.* FROM product a LEFT JOIN product_details b
ON a.id=b.id AND b.weight!=44 AND b.exist=1
WHERE b.id IS NULL;

Matching-Conditions 与 Where-conditions 之战

如果把基本的查询条件放在 ON 子句中,把剩下的否定条件放在 WHERE 子句中,那么你会获得相同的结果。

SELECT a.* FROM product a LEFT JOIN product_details b
ON a.id=b.id AND b.weight!=44 AND b.exist=0
WHERE b.id IS NULL;

可以改为:

SELECT a.* FROM product a LEFT JOIN product_details b
ON a.id=b.id
WHERE b.id is null OR b.weight=44 OR b.exist=1;

又如:

SELECT a.* FROM product a LEFT JOIN product_details b
ON a.id=b.id AND b.weight!=44 AND b.exist!=0
WHERE b.id IS NULL;

可以改为:

SELECT a.* FROM product a LEFT JOIN product_details b
ON a.id=b.id
WHERE b.id is null OR b.weight=44 OR b.exist=0;

只需要第一个表中的数据的话,这些查询会返回相同的结果集。

有一种情况就是,如果你从 LEFT JOIN的表中检索数据时,查询的结果就不同了。

如前所述,WHERE 子句是在匹配阶段之后用来过滤的。

示例

SELECT * FROM product a LEFT JOIN product_details b
ON a.id=b.id AND b.weight!=44 AND b.exist=1
WHERE b.id is null; SELECT * FROM product a LEFT JOIN product_details b
ON a.id=b.id
WHERE b.id IS NULL OR b.weight=44 OR b.exist=0;

注:

如果使用 LEFT JOIN 来寻找在一些表中不存在的记录,需要做下面的测试:WHERE 部分的 col_name IS NULL(其中 col_name 列被定义为 NOT NULL),MYSQL 在查询到一条匹配 LEFT JOIN 条件后将停止搜索更多行(在一个特定的组合键下)

关于 MySQL LEFT JOIN 不可不知的事的更多相关文章

  1. ES6 你可能不知道的事 – 基础篇

    序 ES6,或许应该叫 ES2015(2015 年 6 月正式发布),对于大多数前端同学都不陌生. 首先这篇文章不是工具书,不会去过多谈概念,而是想聊聊关于每个特性 你可能不知道的事,希望能为各位同学 ...

  2. MySQL中join的用法

    近期用phpcms v9做项目,初期没有问题,后期随着数据量的增大,phpcms v9后台出现的栏目更新不动的情况,初期我以为是程序的问题,进行了程序排查,没有发现任何问题,登录上centos服务器后 ...

  3. Java你可能不知道的事(3)HashMap

    概述 HashMap对于做Java的小伙伴来说太熟悉了.估计你们每天都在使用它.它为什么叫做HashMap?它的内部是怎么实现的呢?为什么我们使用的时候很多情况都是用String作为它的key呢?带着 ...

  4. java你可能不知道的事(2)--堆和栈

    在java语言的学习和使用当中你可能已经了解或者知道堆和栈,但是你可能没有完全的理解它们.今天我们就一起来学习堆.栈的特点以及它们的区别.认识了这个之后,你可能对java有更深的理解. Java堆内存 ...

  5. MySQL Left Join,Right Join

    魂屁,东西发这里了关于Left Join,Right Join的 在讲MySQL的Join语法前还是先回顾一下联结的语法,呵呵,其实连我自己都忘得差不多了,那就大家一起温习吧(如果内容有错误或有疑问, ...

  6. MySQL Full Join的实现

    MySQL Full Join的实现 由于MySQL不支持FULL JOIN,以下是替代方法 left join + union(可去除反复数据)+ right join select * from ...

  7. overflow:hidden 你所不知道的事

    overflow:hidden 你所不知道的事 overflow:hidden这个CSS样式是大家常用到的CSS样式,但是大多数人对这个样式的理解仅仅局限于隐藏溢出,而对于清除浮动这个含义不是很了解. ...

  8. mysql left join

    MySQL左连接不同于简单连接.MySQL LEFT JOIN提供该表额外字段在左侧. 如果使用LEFT JOIN,得到的所有记录的匹配方式相同, 在左边表中得到的每个记录不匹配也会有一个额外的记录. ...

  9. MySQL的JOIN(一):用法

    JOIN的含义就如英文单词"join"一样,连接两张表,大致分为内连接,外连接,右连接,左连接,自然连接.这里描述先甩出一张用烂了的图,然后插入测试数据. CREATE TABLE ...

随机推荐

  1. Gravitee.io Access Management docker-compose运行

    Gravitee.io 官方提供的docker-compose 快速运行的方式 默认ui 账户 admin adminadmin 环境准备 docker-compose 文件 # # Copyrigh ...

  2. heptio scanner kubernetes 集群诊断工具部署说明

    heptio scanner 是一款k8s 集群状态的诊断工具,还是很方便的,但是有一点就是需要使用google 的镜像 参考地址 https://scanner.heptio.com/ 部署 kub ...

  3. 【转】iOS编译OpenSSL静态库(使用脚本自动编译)

    原文网址:https://www.jianshu.com/p/651513cab181 本篇文章为大家推荐两个脚本,用来iOS系统下编译OpenSSL通用库,如果想了解编译具体过程,请参看<iO ...

  4. 在单文件组件中,引入安装模块里的css的2种方式:script中引入、style中引入

    在单文件组件中,引入安装模块里的css的2种方式:script中引入.style中引入 1.script中引入 <script> import 'bulma/css/bulma.css' ...

  5. 代码问题: 【ADNet】

    [ADNet]: Yoo S, Yun K, Choi J Y. Action-Decision Networks for Visual Tracking with Deep Reinforcemen ...

  6. plsql远程访问数据库 解决ora-12541:TNS:无监听程序

    今天在windows server 2012上安装了一个oracle 11g的数据库,但是安装 完成以后发现在我的机器上访问数据库出现错误,ora-12541:TNS:无监听程序. 后来查询了很多资料 ...

  7. Zookeeper 重连机制

    Zookeeper 重连机制 public class ZKConnectSessionWatcher implements Watcher { public final static String ...

  8. ecstore-ftp设置,不能上传文件

    某些主机居然不能上传,ftp改成127.0.0.1即可 ftp地址改成127.0.0.1即可...

  9. 协程实现多并发socket,跟NGINX一样

    server: #!/usr/bin/env python # -*- coding: utf-8 -*- # author aliex-hrg import gevent from gevent i ...

  10. GDI+ 库

    uses Winapi.GDIPAPI, Winapi.GDIPOBJ{, Winapi.GDIPUTIL}; procedure TForm1.FormPaint(Sender: TObject); ...