[转]Mysql Join语法解析与性能分析

转自:http://www.cnblogs.com/BeginMan/p/3754322.html

 

一.Join语法概述

join 用于多表中字段之间的联系,语法如下:

... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona

table1:左表;table2:右表。

JOIN 按照功能大致分为如下三类:

INNER JOIN(内连接,或等值连接):取得两个表中存在连接匹配关系的记录。

LEFT JOIN(左连接):取得左表(table1)完全记录,即是右表(table2)并无对应匹配记录。

RIGHT JOIN(右连接):与 LEFT JOIN 相反,取得右表(table2)完全记录,即是左表(table1)并无匹配对应记录。

注意:mysql不支持Full join,不过可以通过UNION 关键字来合并 LEFT JOIN 与 RIGHT JOIN来模拟FULL join.

接下来给出一个列子用于解释下面几种分类。如下两个表(A,B)

mysql> select A.id,A.name,B.name from A,B where A.id=B.id;
+----+-----------+-------------+
| id | name | name | +----+-----------+-------------+ | 1 | Pirate | Rutabaga | | 2 | Monkey | Pirate | | 3 | Ninja | Darth Vader | | 4 | Spaghetti | Ninja | +----+-----------+-------------+ 4 rows in set (0.00 sec)

二.Inner join

内连接,也叫等值连接,inner join产生同时符合A和B的一组数据。

mysql> select * from A inner join B on A.name = B.name;
+----+--------+----+--------+
| id | name   | id | name | +----+--------+----+--------+ | 1 | Pirate | 2 | Pirate | | 3 | Ninja | 4 | Ninja | +----+--------+----+--------+

三.Left join

mysql> select * from A left join B on A.name = B.name;
#或者:select * from A left outer join B on A.name = B.name; +----+-----------+------+--------+ | id | name | id | name | +----+-----------+------+--------+ | 1 | Pirate | 2 | Pirate | | 2 | Monkey | NULL | NULL | | 3 | Ninja | 4 | Ninja | | 4 | Spaghetti | NULL | NULL | +----+-----------+------+--------+ 4 rows in set (0.00 sec)

left join,(或left outer join:在Mysql中两者等价,推荐使用left join.)左连接从左表(A)产生一套完整的记录,与匹配的记录(右表(B)) .如果没有匹配,右侧将包含null。

如果想只从左表(A)中产生一套记录,但不包含右表(B)的记录,可以通过设置where语句来执行,如下:

mysql> select * from A left join B on A.name=B.name where A.id is null or B.id is null; +----+-----------+------+------+ | id | name | id | name | +----+-----------+------+------+ | 2 | Monkey | NULL | NULL | | 4 | Spaghetti | NULL | NULL | +----+-----------+------+------+ 2 rows in set (0.00 sec)

同理,还可以模拟inner join. 如下:

mysql> select * from A left join B on A.name=B.name where A.id is not null and B.id is not null; +----+--------+------+--------+ | id | name | id | name | +----+--------+------+--------+ | 1 | Pirate | 2 | Pirate | | 3 | Ninja | 4 | Ninja | +----+--------+------+--------+ 2 rows in set (0.00 sec)

求差集:

根据上面的例子可以求差集,如下:

SELECT * FROM A LEFT JOIN B ON A.name = B.name WHERE B.id IS NULL union SELECT * FROM A right JOIN B ON A.name = B.name WHERE A.id IS NULL; # 结果 +------+-----------+------+-------------+ | id | name | id | name | +------+-----------+------+-------------+ | 2 | Monkey | NULL | NULL | | 4 | Spaghetti | NULL | NULL | | NULL | NULL | 1 | Rutabaga | | NULL | NULL | 3 | Darth Vader | +------+-----------+------+-------------+

四.Right join

mysql> select * from A right join B on A.name = B.name;
+------+--------+----+-------------+
| id   | name   | id | name        |
+------+--------+----+-------------+
| NULL | NULL   |  1 | Rutabaga | | 1 | Pirate | 2 | Pirate | | NULL | NULL | 3 | Darth Vader | | 3 | Ninja | 4 | Ninja | +------+--------+----+-------------+ 4 rows in set (0.00 sec)

同left join。

五.Cross join

cross join:交叉连接,得到的结果是两个表的乘积,即笛卡尔积

笛卡尔(Descartes)乘积又叫直积。假设集合A={a,b},集合B={0,1,2},则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1), (b,2)}。可以扩展到多个集合的情况。类似的例子有,如果A表示某学校学生的集合,B表示该学校所有课程的集合,则A与B的笛卡尔积表示所有可能的选课情况。

mysql> select * from A cross join B;
+----+-----------+----+-------------+
| id | name      | id | name        |
+----+-----------+----+-------------+
|  1 | Pirate    |  1 | Rutabaga | | 2 | Monkey | 1 | Rutabaga | | 3 | Ninja | 1 | Rutabaga | | 4 | Spaghetti | 1 | Rutabaga | | 1 | Pirate | 2 | Pirate | | 2 | Monkey | 2 | Pirate | | 3 | Ninja | 2 | Pirate | | 4 | Spaghetti | 2 | Pirate | | 1 | Pirate | 3 | Darth Vader | | 2 | Monkey | 3 | Darth Vader | | 3 | Ninja | 3 | Darth Vader | | 4 | Spaghetti | 3 | Darth Vader | | 1 | Pirate | 4 | Ninja | | 2 | Monkey | 4 | Ninja | | 3 | Ninja | 4 | Ninja | | 4 | Spaghetti | 4 | Ninja | +----+-----------+----+-------------+ 16 rows in set (0.00 sec) #再执行:mysql> select * from A inner join B; 试一试 #在执行mysql> select * from A cross join B on A.name = B.name; 试一试

实际上,在 MySQL 中(仅限于 MySQL) CROSS JOIN 与 INNER JOIN 的表现是一样的,在不指定 ON 条件得到的结果都是笛卡尔积,反之取得两个表完全匹配的结果。
INNER JOIN 与 CROSS JOIN 可以省略 INNER 或 CROSS 关键字,因此下面的 SQL 效果是一样的:

... FROM table1 INNER JOIN table2
... FROM table1 CROSS JOIN table2
... FROM table1 JOIN table2

六.Full join

mysql> select * from A left join B on B.name = A.name -> union -> select * from A right join B on B.name = A.name;
+------+-----------+------+-------------+
| id   | name      | id   | name        |
+------+-----------+------+-------------+
|    1 | Pirate    |    2 | Pirate | | 2 | Monkey | NULL | NULL | | 3 | Ninja | 4 | Ninja | | 4 | Spaghetti | NULL | NULL | | NULL | NULL | 1 | Rutabaga | | NULL | NULL | 3 | Darth Vader | +------+-----------+------+-------------+ 6 rows in set (0.00 sec)

全连接产生的所有记录(双方匹配记录)在表A和表B。如果没有匹配,则对面将包含null。

七.性能优化

1.显示(explicit) inner join VS 隐式(implicit) inner join

如:

select * from
table a inner join table b on a.id = b.id;

VS

select a.*, b.*
from table a, table b
where a.id = b.id;

我在数据库中比较(10w数据)得之,它们用时几乎相同,第一个是显示的inner join,后一个是隐式的inner join。

参照:Explicit vs implicit SQL joins

2.left join/right join VS inner join

尽量用inner join.避免 LEFT JOIN 和 NULL.

在使用left join(或right join)时,应该清楚的知道以下几点:

(1). on与 where的执行顺序

ON 条件(“A LEFT JOIN B ON 条件表达式”中的ON)用来决定如何从 B 表中检索数据行。如果 B 表中没有任何一行数据匹配 ON 的条件,将会额外生成一行所有列为 NULL 的数据,在匹配阶段 WHERE 子句的条件都不会被使用。仅在匹配阶段完成以后,WHERE 子句条件才会被使用。它将从匹配阶段产生的数据中检索过滤。

所以我们要注意:在使用Left (right) join的时候,一定要在先给出尽可能多的匹配满足条件,减少Where的执行。如:

PASS

select * from A
inner join B on B.name = A.name left join C on C.name = B.name left join D on D.id = C.id where C.status>1 and D.status=1;

Great

select * from A
inner join B on B.name = A.name left join C on C.name = B.name and C.status>1 left join D on D.id = C.id and D.status=1

从上面例子可以看出,尽可能满足ON的条件,而少用Where的条件。从执行性能来看第二个显然更加省时。

(2).注意ON 子句和 WHERE 子句的不同

如作者举了一个列子:

mysql> SELECT * FROM product LEFT JOIN product_detailsON (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的数据中过滤掉不符合条件的数据行。

(3).尽量避免子查询,而用join

往往性能这玩意儿,更多时候体现在数据量比较大的时候,此时,我们应该避免复杂的子查询。如下:

PASS

insert into t1(a1) select b1 from t2 where not exists(select 1 from t1 where t1.id = t2.r_id); 

Great

insert into t1(a1)  
select b1 from t2  
left join (select distinct t1.id from t1 ) t1 on t1.id = t2.r_id where t1.id is null; 

这个可以参考mysql的exists与inner join 和 not exists与 left join 性能差别惊人

补充:MySQL STRAIGHT_JOIN 与 NATURAL JOIN的使用

感谢网友** 折翼的鸟** 给出的提醒。

长话短说:straight_join实现强制多表的载入顺序,从左到右,如:

...A straight_join B on A.name = B.name 

straight_join完全等同于inner join 只不过,join语法是根据“哪个表的结果集小,就以哪个表为驱动表”来决定谁先载入的,而straight_join 会强制选择其左边的表先载入。

往往我们在分析mysql处理性能时,如(Explain),如果发现mysql在载入顺序不合理的情况下,可以使用这个语句,但往往mysql能够自动的分析并处理好。

更多内容参考:MySQL STRAIGHT_JOIN 与 NATURAL JOIN
和MySQL优化的奇技淫巧之STRAIGHT_JOIN

八.参考:

A Visual Explanation of SQL Joins

五种提高 SQL 性能的方法

关于 MySQL LEFT JOIN 你可能需要了解的三点

转载于:https://www.cnblogs.com/Andy-Lv/p/5363696.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/271984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css radial-gradient 径向渐变基本语法与使用

在之前的文章《深入理解Css linear-gradient线性渐变》我们了解了CSS中的线性渐变,本文将介绍CSS中的另一种渐变———径向渐变(Radial Gradient): CSS中的径向渐变(Radial Gradient)允许你创建从一个颜色…

华为鸿蒙系统技术细节盘点

面对安卓的限制,华为似乎十分淡定从容,丝毫都不慌,华为的底气,很大原因来自华为自研的操作系统——鸿蒙系统!鸿蒙系统刚提出来的时候就在各界媒体中炸开了花,花粉们对其关注程度也是只增不减,那…

spring boot2.x设置session有效时间_Spring 源码解析 Scopes 之 Request 、Session 、Application...

(给ImportNew加星标,提高Java技能)转自:开源中国,作者:麦克斯链接:my.oschina.net/wang5v/blog/3017934Request、Session、Application概念在这篇Spring源码解析-Singleton Scope(单例)和Prototype Scope(多例)博客中介…

学习笔记~~~~~TreeMap

TreeMap继承了AbstractMap类,实现了NavigableMap、Cloneable、Serializable 接口 TreeMap也是一个很常用的map实现类,因为他具有一个很大的特点就是会对Key进行排序,使用了TreeMap存储键值对,再使用iterator进行输出时&#xff0c…

程序员别再迷茫,赚钱,方法比你想的更多

每次打开公号,扑面而来一阵阵焦虑:95后毕业3个月就买房,你的同龄人正在抛弃你毕业3年,年薪超100万:赚钱,是一种修行一线城市财务自由门槛2.9亿,看看你还差多少说来说去就是,牛人跑得…

Mac 创建本地Mysql_2018-09-25:mac下创建本地数据库mysql

问题:如何在mac系统下,创建本地数据库mysql?过程:1.安装brew install mysql2.启动mysql过程中遇到的问题:(1)ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock (2)解决过程&am…

.NET Core 学习资料精选:入门

开源跨平台的.NET Core,还没上车的赶紧的,来不及解释了……本系列文章,主要分享一些.NET Core比较优秀的社区资料和微软官方资料。我进行了知识点归类,让大家可以更清晰的学习.NET Core。首先感谢资料原作者的贡献。第一篇&#x…

学习笔记~~~~~Set接口实现

Java中提供了HashSet、TreeSet、LinkedHashSet三种常用的Set实现,以下具体分析它们的用法和性能。 我们使用Set的原因是Set集合不包含重复元素,HashSet、TreeSet和LinkedHashSet三种类型什么时候使用它们,使用哪个这是一个很重要的选择性问题…

15句乔布斯经典语录(中英文)

1.Life is brief, and then you die, you know?人生短暂,过着过着你就没了,明白么?2.Innovation distinguishes between a leader and a follower.领袖和跟风者的区别就在于创新。3.Were here to put a dent in the universe. Oth…

mysql一些常用操作_mysql的一些常用操作(一)

1.启动Mysql服务net start mysql2.进入mysql环境中,由于自己没有设置密码,直接回车进入即可(要将bin加入到环境变量path中)mysql -u root -p3.创建一个数据库create database db_test default character set utf8 collate utf8_general_ci;显示数据库&am…

关于程序员的脑筋急转弯(附答案)

1、程序猿最常去的是哪间酒吧?2、程序猿什么情况下会选择离职?3、0是假,1是真,请问这是真还是假?4、你怎样才能知道一个计算机科学家是内向还是外向的?5、为什么大部分Java程序员都是戴眼镜的?6…

怎么确保一个集合不能被修改

集合(map,set,list)都是引用类型,所以我们如果用final修饰的话,集合里面的内容还是可以修改的。 可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛…

以下十种性格的人不适合做程序员,你​赞同吗? ​

目录 1. 宁愿参加培训,也不愿意自学 2. 喜欢正常的上下班时间 3. 喜欢正常加薪而不是跳槽 4. 无法和同事和睦共处 5. 容易垂头丧气 6. 思想保守,不考虑他人建议 7. 不注重细节 8. 没有工作荣誉感 9. 不能做到三思而后行 10. 不喜欢极客类型的人 以下十种…

Iterator与ListIterator有什么区别

Iterator与ListIterator区别如下: 1、Iterator是ListIterator的父接口。 2、Iterator是单列集合(Collection)公共取出容器中元素的方式。 对于List,Set都通用。 而ListIterator是List集合的特有取出元素方式。 3、Iterator中具备的功能只有hashNext(),ne…

IT行业17条经典语录

1. 手表定律:一个人有一只表时,可以知道现在是几点钟,当他拥有两只表时,却无法确定。所以,对于任何一件事情,不能同时设置两个不同的目标;对于一个人,也不能同时选择两种不同的价值…

mysql_install_db卸载_MySQL数据库的卸载与安装

MySQL数据库的卸载与安装MySQL的完全卸载因为不知道什么原因,电脑里同时存在两个版本的mysql,所以决定卸载重新安装,但是大家都说MySQL很难清除干净,所以特地查找完全卸载MySQL的方法。首先,快捷键winr输入regedit进入…

前端:QuickJS到底能干什么

QuickJS 是一个轻量且可嵌入的 JavaScript 引擎,它支持 ES2019 规范,包括 ES module、异步生成器以及 proxies。除此之外,还支持可选的数学扩展,例如大整数(BigInt)、大浮点数(BigFloat)和运算符重载。主要特点:轻量而…

随机存取是什么意思_手机小白必看!12GB+256GB,同样是GB,它们到底有什么不同?...

导语本文适合对电子产品有深度兴趣的小白,详细介绍了信息世界的数据计量单位,以及RAM与ROM的,文末给出了购机建议,建议不了解手机或者说半懂的同学观看。看完如果您觉得还可以的话,点赞关注,给小编一个鼓励…

ES6 解构赋值的用法笔记

1、概念:解构赋值可以理解为对赋值运算符的一种扩展。它主要针对数组或者对象进行模式匹配,然后对模式中的变量进行赋值。2、特性:采用ES6解构赋值的方式可以代码的可读性更高、代码书写更加简洁、清晰。3、解构模型:分为解构源、…

收集12个经典的程序员段子

1bug 跟蚊子的相似之处:1、不知道藏在哪里。2、不知道有多少。3、总是在你即将睡觉休息的时候出现。2A:最近在看《一拳超人》,觉得咱们程序猿跟埼玉老师有点像啊!B:哪里像了?A:越秃越强&#xf…