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. Flask--信号 blinker

    Flask--信号 blinker Flask框架中的信号基于blinker,可以让开发者在flask请求过程中 定制一些用户行为执行. 在请求前后,模板渲染前后,上下文前后,异常 的时候 安装: p ...

  2. 说说geotools中坐标转换那点事

    概述: 本文说说geotools中坐标转换的那点事情,以WGS84和web墨卡托相互转换为例. 效果: 转换前 转换后 单个Geometry转换 实现代码: package com.lzugis.ge ...

  3. Electron 打包时下载 xxx-electron-v1.6.8--x64.zip 文件出错

    Electron 打包时下载 xxx-electron-v1.6.8--x64.zip 文件出错 今天在windows上打包其它平台的Electron应用的时候,由于是第一次,所以总是下载 xxx-e ...

  4. 旧书重温:0day2【10】第五章 堆溢出利用2

    好久没有发帖子啦!最近一直很忙!但是还是抽空学习啦下! 前段时间匆匆忙忙的把0day2上的堆溢出实验做啦! 可能当时太浮躁啦,很多细节没注意!结果:实验结果很不满意!所以就有啦这一篇!! 上一篇是发布 ...

  5. 同一局域网环境下的arp欺骗和中间人攻击(mac)

    最近读了一篇有关arp欺骗和中间人攻击的文章,于是乎就想着自己实现一下,顺便验证下微信在回话劫持后的安全性. 1.本机环境 Macbook Air:OS X 10.11 El Captain 2.推荐 ...

  6. bzoj 3173 最长上升子序列

    Written with StackEdit. Description 给定一个序列,初始为空.现在我们将\(1\)到\(N\)的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字, ...

  7. NSNotificationCenter 通知中心传值

    1.NSNotification 这个类可以理解为一个消息对象,其中有三个成员变量. 这个成员变量是这个消息对象的唯一标识,用于辨别消息对象. @property (readonly, copy) N ...

  8. UVA11796 Dog Distance

    题意 PDF 分析 问题可以转化为小问题,即两条狗分别在线段上运动. 然后用相对运动知识可以认为甲不动,乙在线段上运动. 小问题就转化为点到线段的最小或最大距离. 时间复杂度\(O(I \times ...

  9. el表达式的坑

    为了写作业,又用回去了jstl,有几个坑重复坑了 1,导入包 <dependency> <groupId>javax.servlet</groupId> <a ...

  10. css3中做3D导航栏

    看别人做的一个3D导航栏,觉得很厉害,这里先保存下来,后面有时间好好分析一下: <!doctype html> <html lang="en"> <h ...