给出两个表,A和B,A和B表的数据量,

当A小于B时,用exists
select * from A where exists (select * from B where A.id=B.id)
exists的实现,相当于外表循环,每次循环对内表进行查询?
for i in A
for j in B
if j.id == i.id then ....
相反,如果A大于B的时候,则用in
select * from A where id in (select id from B)
这种在逻辑上类似于
for i in B
for j in A
if j.id == i.id then ....
然后MySQL实现in,使用了hash join (哈希连接)。我猜想和逻辑上的两层循环在工作原理上是不一样的。
 
经过我的实际测试
SELECT * FROM user u where exists  (SELECT 0 FROM plan p where p.user_id=u.user_id )
0.188 sec / 6.234 sec
 
SELECT * FROM user u where  user_id in  (SELECT user_id FROM plan)
0.015 sec / 0.000 sec
使用in的可谓是瞬间的
 
其中Plan表有3300行记录,User表有3800,数据量恰好接近。
所以那个伪码(两层循环只能帮助理解),和实际情况差距很大。否则二者应该非常接近才对。
 
  Duration Fetch Rows  SQL
exists 0.17 6.2 924 SELECT * FROM user u where exists  (SELECT 0 FROM plan p where p.user_id=u.user_id )
in 0.01 0.0 924 SELECT * FROM user u where  user_id in  (SELECT user_id FROM plan)
 虽然数据量很小,但是可以想象,如果我们用exists,可能会非常糟糕,难怪都建议用in。
 
 
Hash Join
1. 什么事Hash Join。据说是Oracle7.3以后出现的一种连接技术,估计就是要解决本质形如两层循环的工作方式吧(猜测)
那么,select * from A where id in (select id from B),我猜测实际处理中,会变为。
dict = hash(B)
for i in A
if i in dict then ...

如果如此,当然会快很多了。并且之前提到过,B表小的时候,用in,在这里也正好被放到一个Hash表里面。

更多引用:
1、hash join 就是哈希连接,当一个表或多个表上没有索引时,或者数据库服务器必须从所有连接表读取大量行的时候,就用这种方法。
2、orcale7.3以后才出来一种hash join的新的连接技术,hash join只能用于相等连接。
3、相对于以前的nested loop join连接技术,hash join更适合处理大型结果集,而且不需要在驱动表上建索引。
4、hash算法就是在两个表连接的时候,首先要区分大表和小表,小表用S来表示,大表用B来表示,然后就把小表S表放在一个内存的hash table中。
5、若这个小表S表还是太大,放不进去hash table中,就应该对这个hash table要分区,对于大表B表也是要放在内存中的,若B表也是太大,那也要进行分区处理。
6、这样的话,S表就分开若干个区,B表也分成若干个区,这样就开始各个分区之间的互连。
7、除了hash table的分区后,还有一个就是hash area内存的感念,就是逐个处理S与B的各个子集相连情况,首先把小表放在内存里,内存是高速缓冲区,速度快,大表放在慢速的存储区内,从慢速区内读条数据后,再与高速区的做循环,这样肯定会很快的,反过来就慢了,所以优化始终强调小表套大表,小表在高速缓冲区内执行,大表在慢速区放着。
8、S与B的两两相连时,会出现脚色互换的的,当B表的某个分区少的话,那这个小B表进如hash area中做主表。
9、hash join的核心就是确认小表为驱动表,在算法执行之前大小表都要拆分,会有两者角色互换的情况,关键是看谁的数量小,小的进高速内存跑循环,大的在外面有IO处理过程。
10、总之hash join适合于小表与大表相连,返回大型结果集的连接。

从这里摘的:http://blog.sina.com.cn/s/blog_648760e30101b1uh.html

 
Not in
1. 不要使用Not in,解决办法无外乎加索引,这样会快一些,然后效果未必明显,一般是改用Left join。
select * from A where id in (select id from B)
===>
select * from A left join B on a.id=b.id where b.id is null
 
关键是为什么in很快,not in很慢,
首先,not in并非是我们想象中的那样,subquery只求一次值,然后外层select判断是否in这个集合。
事实上,subquery会被执行N次。(好吧,疯了吧,然而我没有想清楚,为什么不能(设计成)执行一次就OK了。)
参考:https://stackoverflow.com/questions/12728654/why-is-this-mysql-query-with-the-not-in-statement-so-slow/12728981#12728981
 
 

小表驱动大表, 兼论exists和in的更多相关文章

  1. 查询优化--小表驱动大表(In,Exists区别)

    Mysql 系列文章主页 =============== 本文将以真实例子来讲解小表驱动大表(In,Exists区别) 1 准备数据 1.1 创建表.函数.存储过程 参照  这篇(调用函数和存储过程批 ...

  2. 6.2 小表驱动大表(exists的应用)

    1. 优化原则:小表驱动大表,即小数据集驱动大数据集. select * from A where id in (select id from B) 等价于: for select id from B ...

  3. Mysql优化原则_小表驱动大表IN和EXISTS的合理利用

    //假设一个for循环 ; $i < ; $i++) { ; $i < ; $j++) { } } ; $i < ; $i++) { ; $i < ; $j++) { } } ...

  4. MySQL高级知识(十六)——小表驱动大表

    前言:本来小表驱动大表的知识应该在前面就讲解的,但是由于之前并没有学习数据批量插入,因此将其放在这里.在查询的优化中永远小表驱动大表. 1.为什么要小表驱动大表呢 类似循环嵌套 for(int i=5 ...

  5. MySql 小表驱动大表

    在了解之前要先了解对应语法 in 与 exist. IN: select * from A where A.id in (select B.id from B) in后的括号的表达式结果要求之输出一列 ...

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

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

  7. 3.mysql小表驱动大表的4种表连接算法

    小表驱动大表 1.概念 驱动表的概念是指多表关联查询时,第一个被处理的表,使用此表的记录去关联其他表.驱动表的确定很关键,会直接影响多表连接的关联顺序,也决定了后续关联时的查询性能. 2.原则 驱动表 ...

  8. 【Spark调优】小表join大表数据倾斜解决方案

    [使用场景] 对RDD使用join类操作,或者是在Spark SQL中使用join语句时,而且join操作中的一个RDD或表的数据量比较小(例如几百MB或者1~2GB),比较适用此方案. [解决方案] ...

  9. hive join 优化 --小表join大表

    1.小.大表 join 在小表和大表进行join时,将小表放在前边,效率会高.hive会将小表进行缓存. 2.mapjoin 使用mapjoin将小表放入内存,在map端和大表逐一匹配.从而省去red ...

随机推荐

  1. 使用OpenCV&&C++进行模板匹配.

    一:课程介绍 1.1:学习目标 学会用imread载入图像,和imshow输出图像. 用nameWindow创建窗口,用createTrackbar加入滚动条和其回调函数的写法. 熟悉OpenCV函数 ...

  2. u-boot平台的建立,驱动的添加,索引的创建,命令机制的实现.

    一:U-boot移植前建立自己的平台: 关注的相关文件:1.u-boot- 2010.03/board/samsung/ //这个目录下需要创建自己的板级目录fsc100 cp –a smdkc100 ...

  3. GridView获取CheckBox的值及所在列的ID

    //根据GridView的列数进行循环 ; i < GridView1.Rows.Count; i++) { //检查是否有ID为CheckBox1的CheckBox控件,如果有就赋值给Chec ...

  4. svn/git的diff、patch

    svn/git的diff.patch 前几天,正当我突突的写代码,企业微信嘀嘀一声响”在不,过来帮我看个bug”.本人一向助人为乐,高兴的冲了过去,然后就开始了一段长达1分钟的问题描述.很明显,此同学 ...

  5. mysql登录不了及修改密码

    安装mariadb,默认是无密码的,但一般是指要设置密码的.在设置密码时出现各种问题,可能还是不太明白其原理. 一下我尝试了两种方法,但都失败了.下面这两个是我尝试的方法: 一.网上最多的方法是 1. ...

  6. C/C++代码覆盖率生成

    初始状态下只有一个源代码文件 nosoul@linux:testCov> ls test.c nosoul@linux:testCov> 第一步:编译.链接.执行可执行文件 gcc -o ...

  7. Autorun.inf文件(2):改变硬盘分区图标

    改变F盘图标. 原理:在f盘下新建一个Autorun.inf文件,文件内容是 [AutoRun]icon=favicon.ico准备名为favicon.ico图标文件,将其放在工程目录里,设计程序将它 ...

  8. Python的名字绑定

    Python的名字绑定 在Python中,对象是通过名字进行关联和引用的.Python通过名字绑定操作来引入名字. Python中的所谓的代码块就是一段作为执行单元的程序.比如:模块.函数.类定义.在 ...

  9. js中的类数组对象---NodeList

    动态 NodeList 这是文档对象模型(DOM,Document Object Model)中的一个大坑. NodeList 对象(以及 HTML DOM 中的 HTMLCollection对象)是 ...

  10. Android中ExpandableListView的使用

    ExpandableListView是Android中可以实现下拉list的一个控件,具体的实现方法如下: 首先:在layout的xml文件中定义一个ExpandableListView < L ...