原文地址:http://www.oschina.net/question/89964_65912?sort=default&p=3#answers

即使你认为自己已对 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 子句条件才会被使用。它将从匹配阶段产生的数据中检索过滤。

让我们看一个 LFET JOIN 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
2
3
4
5
6
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;

用例子来理解最好不过了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> SELECT * FROM product LEFT JOIN product_details
       ON (product.id = product_details.id)
       AND product_details.id=2;
+----+--------+------+--------+-------+
| id | amount | id   | weight | exist |
+----+--------+------+--------+-------+
|  1 |    100 | NULL |   NULL NULL |
|  2 |    200 |    2 |     22 |     0 |
|  3 |    300 | NULL |   NULL NULL |
|  4 |    400 | NULL |   NULL NULL |
+----+--------+------+--------+-------+
4 rows in set (0.00 sec)
  
mysql> SELECT * FROM product LEFT JOIN product_details
       ON (product.id = product_details.id)
       WHERE product_details.id=2;
+----+--------+----+--------+-------+
| id | amount | id | weight | exist |
+----+--------+----+--------+-------+
|  2 |    200 |  2 |     22 |     0 |
+----+--------+----+--------+-------+
1 row in set (0.01 sec)

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

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

再来看一些示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql>
mysql> SELECT * FROM product LEFT JOIN product_details
       ON product.id = product_details.id
       AND product.amount=100;
+----+--------+------+--------+-------+
| id | amount | id   | weight | exist |
+----+--------+------+--------+-------+
|  1 |    100 | NULL |   NULL NULL |
|  2 |    200 | NULL |   NULL NULL |
|  3 |    300 | NULL |   NULL NULL |
|  4 |    400 | NULL |   NULL NULL |
+----+--------+------+--------+-------+
4 rows in set (0.00 sec)

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

1
2
3
4
5
6
7
8
9
10
11
12
mysql> SELECT * FROM product LEFT JOIN product_details
       ON (product.id = product_details.id)
       AND product.amount=200;
+----+--------+------+--------+-------+
| id | amount | id   | weight | exist |
+----+--------+------+--------+-------+
|  1 |    100 | NULL |   NULL NULL |
|  2 |    200 |    2 |     22 |     0 |
|  3 |    300 | NULL |   NULL NULL |
|  4 |    400 | NULL |   NULL NULL |
+----+--------+------+--------+-------+
4 rows in set (0.01 sec)

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

使用 WHERE ... IS NULL 子句的 LEFT JOIN

当你使用 WHERE ... IS NULL 子句时会发生什么呢?

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

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

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

  • 将 IS NULL 作为否定匹配条件
  • 使用 !(A and B) == !A OR !B 逻辑判断

看看下面的示例:

1
2
3
4
5
6
7
8
9
10
11
mysql> 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;
+----+--------+
| id | amount |
+----+--------+
|  1 |    100 |
|  3 |    300 |
|  4 |    400 |
+----+--------+
3 rows in set (0.00 sec)

让我们检查一下 ON 匹配子句:

1
(a.id=b.id) AND (b.weight!=44) AND (b.exist=0)

我们可以把 IS NULL 子句 看作是否定匹配条件。

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

1
2
3
!( 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表达式一样,其操作数是从左到右求值的。如果第一个参数做够判断操作结果,那么第二个参数便不会被计算求值(短路效果)

看看别的示例:

1
2
3
4
5
6
7
8
9
10
11
12
mysql> 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;
+----+--------+
| id | amount |
+----+--------+
|  1 |    100 |
|  2 |    200 |
|  3 |    300 |
|  4 |    400 |
+----+--------+
4 rows in set (0.00 sec)

Matching-Conditions 与 Where-conditions 之战

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

例如,你可以不这样写:

1
2
3
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;

你可以这样写:

1
2
3
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;
1
2
3
4
5
6
7
8
9
10
11
mysql> 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;
+----+--------+
| id | amount |
+----+--------+
|  1 |    100 |
|  3 |    300 |
|  4 |    400 |
+----+--------+
3 rows in set (0.00 sec)

你可以不这样写:

1
2
3
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;

可以这样写:

1
2
3
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;
1
2
3
4
5
6
7
8
9
10
11
12
mysql> 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;
+----+--------+
| id | amount |
+----+--------+
|  1 |    100 |
|  2 |    200 |
|  3 |    300 |
|  4 |    400 |
+----+--------+
4 rows in set (0.00 sec)

这些查询真的效果一样?

如果你只需要第一个表中的数据的话,这些查询会返回相同的结果集。有一种情况就是,如果你从 LEFT JOIN的表中检索数据时,查询的结果就不同了。

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

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
mysql> 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;
+----+--------+------+--------+-------+
| id | amount | id   | weight | exist |
+----+--------+------+--------+-------+
|  1 |    100 | NULL |   NULL NULL |
|  2 |    200 | NULL |   NULL NULL |
|  3 |    300 | NULL |   NULL NULL |
|  4 |    400 | NULL |   NULL NULL |
+----+--------+------+--------+-------+
4 rows in set (0.00 sec)
  
mysql> 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;
+----+--------+------+--------+-------+
| 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)

总附注:

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

关于MySQL中的left join、on、where的一点深入的更多相关文章

  1. Mysql 中Left/Right join on后面and和where条件查询的差异-Mysql SQL运算符是有优先级

    一.Mysql中Left/Right join on后面and和where条件查询的差异 1.建两张测试表,一张商户定义表.一张商户操作状态明细表 1)商户定义表 CREATE TABLE hope. ...

  2. MySQL中使用INNER JOIN来实现Intersect并集操作

    MySQL中使用INNER JOIN来实现Intersect并集操作 一.业务背景 我们有张表设计例如以下: CREATE TABLE `user_defined_value` ( `RESOURCE ...

  3. MySQL中链接查询inner join与left join使用

    连接查询其实就是对两个表记录做笛卡尔乘积.如果不指定连接条件的话,则会对每行都做笛卡尔乘积,这样最后返回的结果树就会是两个表记录数的乘积:如果指定则,则仅对符合列条件的行进行笛卡尔乘积,并返回结果.在 ...

  4. mysql中的几种join 及 full join问题

    [注意]:Oracle数据库支持full join,mysql是不支持full join的,但仍然可以同过左外连接+ union+右外连接实现 初始化SQL语句:   /*join 建表语句*/   ...

  5. MySQL中基本的多表连接查询教程

    一.多表连接类型1. 笛卡尔积(交叉连接) 在MySQL中可以为CROSS JOIN或者省略CROSS即JOIN,或者使用','  如: SELECT * FROM table1 CROSS JOIN ...

  6. Mysql中的语句优化

    1.EXPLAIN 做MySQL优化,我们要善用EXPLAIN查看SQL执行计划. 下面来个简单的示例,标注(1.2.3.4.5)我们要重点关注的数据: type列,连接类型.一个好的SQL语句至少要 ...

  7. mysql中的JOIN用法总结

    join是mysql中一个基础的关键词,一般在多表连接查询中使用,这里做一下总结 1.JOIN的语法格式 table_references: table_reference [, table_refe ...

  8. MySQL分页优化中的“INNER JOIN方式优化分页算法”到底在什么情况下会生效?

    本文出处:http://www.cnblogs.com/wy123/p/7003157.html 最近无意间看到一个MySQL分页优化的测试案例,并没有非常具体地说明测试场景的情况下,给出了一种经典的 ...

  9. mysql中多个left join子查询写法以及别名用法

    不多说 直接上语句   SELECT     a.id,     a.thumbNail,     a. NAME,     a.marketPrice,     a.memberPrice,     ...

随机推荐

  1. ubuntu登陆出现问题

    手贱改了用户root权限结果登陆时提示system administrator is not allowed to login from this screen(郁闷勒) 这时可以按ctrl+F2进入 ...

  2. Mac下安装LNMP(Nginx+PHP5.6)环境

    [转自:http://avnpc.com/pages/install-lnmp-on-osx] 安装Homebrew 最近工作环境切换到Mac,所以以OS X Yosemite(10.10.1)为例, ...

  3. RecyclerView使用总结

    遇到的异常: java.lang.ExceptionInInitializerError静态块初始化异常 NetworkOnMainThreadException访问网线不能在主线程中进行 我的参考资 ...

  4. 你不知道的Java类

    1.StrSubstitutor StrSubstitutor是一个很特殊的类,在String中使用${key}的方法在形成了一个可配置的模板,new一个HashMap使用HashMap替换Strin ...

  5. YTU 3025: 创建二叉树

    原文链接:https://www.dreamwings.cn/ytu3025/2628.html 3025: 创建二叉树 时间限制: 1 Sec  内存限制: 128 MB 提交: 3  解决: 3 ...

  6. C++中虚析构函数的作用

    我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数.可是,为什么要这样做呢?下面用一个小例子来说明:         有下面的两个类: class ClxBase { public: ...

  7. sqlite与android交互 (封装)

    学android已经有大概一周时间了吧 ,总感觉自己基础不怎么好,只能通过一点一点积累着敲来巩固平常的知识,有的时候就先不封装的敲一遍,再封装上,有些语句真的记不住,虽然知道他是什么意思,于是乎就反复 ...

  8. 浏览器何时发送一个Option请求

    Http Options Method 简而言之,OPTIONS请求方法的主要用途有两个: 1.获取服务器支持的HTTP请求方法: 2.用来检查服务器的性能. CORS(跨域资源共享) CORS是一种 ...

  9. 使用WordPress模板搭建博客系统

    综述: 前端展示:外观--->主题. 功能模块:插件. 遇到的问题: 1:无法加载编辑器文件: 切换下不同的wordPress模板,可能缓存文件有问题. 2:注册功能:密码重设链接无效bug-- ...

  10. Windows里面的hosts文件

    一.什么是Hosts文件? hosts文件是一个用于储存计算机网络中各节点信息的计算机文件.这个文件负责将主机名映射到相应的IP地址.hosts文件通常用于补充或取代网络中DNS的功能.和DNS不同的 ...