IN 是子查询的关键字,JOIN 是连接的关键字,项目开发中经常会使用到多表查询,而子查询与连接正是实现多表查询的重要途径。那两者是怎么运行的?IN与JOIN哪个更好?下面就来分析与比较。


现在有test1与test2两张表,都没有任何像主键,外键那样的约束,且只有一个字段。两张表是非相关的。


现在使用IN关键字实现子查询,test2作为子查询表(外部表):

查看执行计划:


使用JOIN关键字实现连接,同样test2作为外部表:

查看执行计划:


分析:

  1. 使用IN子查询实现多表查询时,从执行计划可以看出,整个查询分成3个部分,id = 1的查询有两个,id = 2的查询有一个。id大的级别高,优先进行查询。id = 2的查询对应的是test2(子查询表)的FTS。然后进行id = 1的查询,同级别的查询从上往下顺序执行。计划中显示这个查询是个子查询(subquery),同时查询test1的时候,使用到join buffer(Blocked Nested Loop),即连接缓冲(阻塞的嵌套循环)。
  2. 使用JOIN连接实现多表查询时,先查询test2表(外部表),几乎与IN的方式一样(FTS),再查询test1表,也与IN的方式一样,都用到了join buffer(Blocked Nested Loop)
  3. 那join buffer(Blocked Nested Loop)究竟是什么意思,我想这篇博客已经解释得很清楚了。http://blog.itpub.net/22664653/viewspace-1692317/
  4. 总结一下,非相关(无索引)的多表查询中,使用IN与JOIN的查询都是先将外部表的查询结果加入到连接缓冲区,再从内部表拿取数据进入缓冲区进行比较(嵌套循环)。查询计划几乎没有区别。但是,IN存在优先级的关系,比JOIN多了一次subquery的查询,在这种情况下,JOIN更优。

现在在test1表中添加主键(索引),在test2表中添加外键约束(索引),两张表是相关的。

进行同样的查询,返回结果是一样的:


查看IN方式的执行计划:


查看JOIN方式的执行计划:


分析:

  1. 现在使用IN方式进行查询时,不再像非相关那样显示子查询subquery了(若是子查询会有不同的优先级),而是有个参照的过程!先借助索引对外部表test2进行扫描;再借助索引对test1进行扫描,其中参照了test2的id列。
  2. 使用JOIN方式也是一样有一个参照的过程!
  3. 这时两种方式的查询也没有用到上面所说的连接缓冲区与阻塞嵌套循环。
  4. 总结一下,当两张表相关(外键相连)时,无论是IN还是JOIN,联合查找都是一个参照的过程。

写到这里,似乎IN与JOIN在表相关(逻辑外键)的时候,并不知道哪个更优,下面就来实践一下。


实际应用:


下面使用MySQL的示例数据库sakila(customer表中有599个顾客信息,主键为customer_id。rental表中有16044行数据,其中的主键为rental_id,外键列customer_id参考customer表中的主键)分别执行IN与JOIN实现多表查询:

IN查询语句:SELECT CONCAT(first_name,last_name) FROM customer WHERE customer_id IN (SELECT customer_id FROM rental WHERE rental_id <=16000);

结果(返回了599条客户名字信息):

慢查询日志:

# Time: 160717 21:17:58
# User@Host: root[root] @ localhost [127.0.0.1] Id: 17
# Query_time: 0.000000 Lock_time: 0.000000 Rows_sent: 599 Rows_examined: 1198
use sakila;
SET timestamp=1468761478;
SELECT CONCAT(first_name,last_name) FROM customer WHERE customer_id IN (SELECT customer_id FROM rental WHERE rental_id <=16000);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

JOIN查询语句:SELECT CONCAT(first_name,last_name) FROM customer AS a INNER JOIN rental AS b ON a.customer_id = b.customer_id WHERE rental_id<=16000;

结果(返回了15995行数据,发现里面有很多重复的名字):

慢查询日志:

# Time: 160717 21:19:17
# User@Host: root[root] @ localhost [127.0.0.1] Id: 18
# Query_time: 0.030000 Lock_time: 0.000000 Rows_sent: 15995 Rows_examined: 16643
SET timestamp=1468761557;
SELECT CONCAT(first_name,last_name) FROM customer AS a INNER JOIN rental AS b ON a.customer_id = b.customer_id WHERE rental_id<=16000;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用DISTINCT关键字去重的JOIN查询语句:SELECT DISTINCT CONCAT(first_name,last_name) FROM customer AS a INNER JOIN rental AS b ON a.customer_id = b.customer_id WHERE rental_id <=16000;

慢查询日志:

# Time: 160717 21:20:31
# User@Host: root[root] @ localhost [127.0.0.1] Id: 19
# Query_time: 0.010000 Lock_time: 0.000000 Rows_sent: 599 Rows_examined: 1797
SET timestamp=1468761631;
SELECT DISTINCT CONCAT(first_name,last_name) FROM customer AS a INNER JOIN rental AS b ON a.customer_id = b.customer_id WHERE rental_id <=16000;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

分析:

  1. 由于rental表的customer_id列作为外键列,参照的是customer表的主键customer_id。因此在该查询上两张表是相关表。上面已经分析了这样的IN与JOIN实现多表查询就不存在连接缓冲与阻塞的嵌套循环。但都是通过参照的关系进行查找。
  2. 通过比较查找时间(SQL效率)与检索行数(磁盘IO),在这种情况下我会选择IN进行查询。

分析比较多表查询中的IN与JOIN的更多相关文章

  1. MySQL数据库之单表查询中关键字的执行顺序

    目录 MySQL数据库之单表查询中关键字的执行顺序 1 语法顺序 2 执行顺序 3 关键字使用语法 MySQL数据库之单表查询中关键字的执行顺序 1 语法顺序 select distinct from ...

  2. SQL多表查询中的分页,字段组合综合实例解析

    原文:http://www.jb51.net/article/28753.htm http://xuzhihong1987.blog.163.com/blog/static/2673158720098 ...

  3. mysql,SQL标准,多表查询中内连接,外连接,自然连接等详解之查询结果集的笛卡尔积的演化

    先附上数据. CREATE TABLE `course` ( `cno` ) NOT NULL, `cname` ) CHARACTER SET utf8 NOT NULL, `ctime` ) NO ...

  4. 了解MySQL联表查询中的驱动表,优化查询,以小表驱动大表

    一.为什么要用小表驱动大表 1.驱动表的定义 当进行多表连接查询时, [驱动表] 的定义为: 1)指定了联接条件时,满足查询条件的记录行数少的表为[驱动表] 2)未指定联接条件时,行数少的表为[驱动表 ...

  5. 【explain】MySQL联表查询中的驱动表

    写在前面 1.不要求每个人一定理解 联表查询(join/left join/inner join等)时的mysql运算过程 2.不要求每个人一定知道线上(现在或未来)哪张表数据量大,哪张表数据量小 3 ...

  6. 【SQL】多表查询中的 外连接 ,on,where

    先简单粗暴给个结论,多表连结查询中,on比where更早起作用,系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行匹配过滤,where后语句为真,则能查询出来,而通过外连接 ...

  7. Oracle的查询-多表查询中的一些概念

    --笛卡尔积 select * from emp e,dept d; --等值连接 select * from emp e,dept d where e.deptno=d.deptno --内连接 s ...

  8. SQL查询中的in与join效率比较

    大多数情况下,程序员比较喜欢使用in来查询符合某些条件的数据,最近在查询某个角色有哪些用户的方法中,使用了in语句: ) FROM baseuser AND BaseUser.Id IN (SELEC ...

  9. 全国排名的问题(linq 的连表查询 等同于sql的left join)

    前言:要获得全国排名,(因为权限问题,显示的数据不是全国的数据,而是某个分区的数据,因此,不能获得数据后排序得到排名) 显示本部的员工积分并且获得在全国的排名. 我的思路:获得显示的员工信息集合1,获 ...

随机推荐

  1. Linux系统下的shutdown命令用于安全的关闭/重启计算机

    Linux系统下的shutdown命令用于安全的关闭/重启计算机,它不仅可以方便的实现定时关机,还可以由用户决定关机时的相关参数.在执行shutdown命令时,系统会给每个终端(用户)发送一条屏显,提 ...

  2. 记录一个因sqlmap导致的错误

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE sqlMap PUBLIC "- ...

  3. macOS Ruby版本需要升级到2.2.2以上

    在安装 Ruby on Rails 时遇到问题,提示依赖 ruby 2.2.2 或更高的版本. ERROR: Error installing rails: activesupport require ...

  4. Compiling OpenGL games with the Flash C Compiler (FlasCC)

    Compiling OpenGL games with the Flash C Compiler (FlasCC) In this article I show how to use the Flas ...

  5. Zookeeper在Dubbo中的作用及Zk集群的选举原理

    转自 : https://blog.csdn.net/zh15732621679/article/details/80723358

  6. asp.net viewstate 数据过大 导致错误

    当在ViewState中放入dataSet的数据量比较大的时候,当再点页面上的控件时,不会返回到后台,并且会出现如下错误: 或者是上面的12030改成500的错误. --解决方法:Viewstate绑 ...

  7. Struts2常用标签总结

    Struts2常用标签总结 一 介绍 1.Struts2的作用 Struts2标签库提供了主题.模板支持,极大地简化了视图页面的编写,而且,struts2的主题.模板都提供了很好的扩展性.实现了更好的 ...

  8. iOS 10 隐私权限设置

    iOS 10 开始对隐私权限更加严格,如果你不设置就会直接崩溃,现在很多遇到崩溃问题了,一般解决办法都是在info.plist文件添加对应的Key-Value就可以了. 以上Value值,圈出的红线部 ...

  9. SQL Server: Top 10 Secrets of a SQL Server Expert

    转载自:http://technet.microsoft.com/en-us/magazine/gg299551.aspx Many companies have downsized their IT ...

  10. 记一次愚蠢的gradle操作

    今晚把工作移植到mac平台,在用gradle命令 exec ./gradlew --parallel --info assembleDebug 打包apk时卡住,gradle一直处于下载状态,过了几分 ...