在我们的工作中可能会遇到这样的情形:

我们需要查询a表里面的数据,但是要以b表作为约束。

举个例子,比如我们需要查询订单表中的数据,但是要以用户表为约束,也就是查询出来的订单的user_id要在用户表里面存在才返回。

表结构和表数据如下:

table1 usertb;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(30) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
+----+-----------+
| id | name |
+----+-----------+
| 1 | panchao |
| 2 | tangping |
| 3 | yinkaiyue |
+----+-----------+

table2 ordertb;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | YES | | NULL | |
| order_name | varchar(50) | YES | | NULL | |
+------------+-------------+------+-----+---------+----------------+
+----+---------+-------------------+
| id | user_id | order_name |
+----+---------+-------------------+
| 1 | 1 | tangping's order |
| 2 | 2 | yinkaiyue's order |
| 3 | 0 | zhangtian's order |
+----+---------+-------------------+

看过表过后,大家在脑海中可能已经想出了很多方法了,对吧。

主要三种方法:left join、in、exists。

我们分别来看看。他们的查询结果和explain的结果。

1、left join:

MariaDB [test]> select * from ordertb a left join usertb b on a.user_id = b.id;
+----+---------+-------------------+------+----------+
| id | user_id | order_name | id | name |
+----+---------+-------------------+------+----------+
| 1 | 1 | tangping's order | 1 | panchao |
| 2 | 2 | yinkaiyue's order | 2 | tangping |
| 3 | 0 | zhangtian's order | NULL | NULL |
+----+---------+-------------------+------+----------+
MariaDB [test]> explain select * from ordertb a left join usertb b on a.user_id= b.id;
+------+-------------+-------+--------+---------------+---------+---------+----------------+------+-------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref      | rows | Extra |
+------+-------------+-------+--------+---------------+---------+---------+----------------+------+-------------+
| 1  | SIMPLE      | a      | ALL  | NULL               | NULL   | NULL    | NULL | 3      |          |
| 1 | SIMPLE | b | eq_ref | PRIMARY | PRIMARY | 4 | test.a.user_id | 1 | Using where |
+------+-------------+-------+--------+---------------+---------+---------+----------------+------+-------------+

2、in:

MariaDB [test]> select * from ordertb where ordertb.user_id in (select id from usertb);
+----+---------+-------------------+
| id | user_id | order_name |
+----+---------+-------------------+
| 1 | 1 | tangping's order |
| 2 | 2 | yinkaiyue's order |
+----+---------+-------------------+
MariaDB [test]> explain select * from ordertb where ordertb.user_id in (select id from usertb);
+------+-------------+---------+--------+---------------+---------+---------+----------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+--------+---------------+---------+---------+----------------------+------+-------------+
| 1 | PRIMARY | ordertb | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
| 1 | PRIMARY | usertb | eq_ref | PRIMARY | PRIMARY | 4 | test.ordertb.user_id | 1 | Using index |
+------+-------------+---------+--------+---------------+---------+---------+----------------------+------+-------------+

3、exists:

MariaDB [test]> select * from ordertb where exists(select 1 from usertb where usertb.id = ordertb.user_id);
+----+---------+-------------------+
| id | user_id | order_name |
+----+---------+-------------------+
| 1 | 1 | tangping's order |
| 2 | 2 | yinkaiyue's order |
+----+---------+-------------------+
MariaDB [test]> explain select * from ordertb where exists(select 1 from usertbwhere usertb.id = ordertb.user_id);
+------+-------------+---------+--------+---------------+---------+---------+----------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+--------+---------------+---------+---------+----------------------+------+-------------+
| 1 | PRIMARY | ordertb | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
| 1 | PRIMARY | usertb | eq_ref | PRIMARY | PRIMARY | 4 | test.ordertb.user_id | 1 | Using index |
+------+-------------+---------+--------+---------------+---------+---------+----------------------+------+-------------+

我们可以看到,这三种查询的explain结果大致相同,唯一不同的是left join中的Extra没有用到Useing Where。说明left join相比于其他两个查询效率要低一些,并且left join中有冗余数据。

我们再来看 in 和 exists ,从表面上来看好像xiaolv一样。其实不然。我们来深入分析一下这两个语句。

1、in。

其中usertb我们用B来代替,ordertb我们用A来代替。

in()只执行一次,它查出B表中的所有id字段并缓存起来.之后,检查A表的user_id是否与B表中的id相等,如果相等则将A表的记录加入结果集中,直到遍历完A表的所有记录. 它的查询过程类似于以下过程

List resultSet=[]; Array A=(select * from A); Array B=(select id from B);
for(int i=0;i<A.length;i++) {    for(int j=0;j<B.length;j++) {       if(A[i].id==B[j].id) {          resultSet.add(A[i]);          break;       }    } } return resultSet;

可以看出,当B表数据较大时不适合使用in(),因为它会B表数据全部遍历一次. 如:A表有10000条记录,B表有1000000条记录,那么最多有可能遍历10000*1000000次,效率很差. 再如:A表有10000条记录,B表有100条记录,那么最多有可能遍历10000*100次,遍历次数大大减少,效率大大提升.

2、exists。

exists()会执行A.length次,它并不缓存exists()结果集,因为exists()结果集的内容并不重要,重要的是结果集中是否有记录,如果有则返回true,没有则返回false. 它的查询过程类似于以下过程

List resultSet=[]; Array A=(select * from A)
for(int i=0;i<A.length;i++) {    if(exists(A[i].id) {    //执行select 1 from B b where b.id=a.id是否有记录返回        resultSet.add(A[i]);    } } return resultSet;

当B表比A表数据大时适合使用exists(),因为它没有那么遍历操作,只需要再执行一次查询就行. 如:A表有10000条记录,B表有1000000条记录,那么exists()会执行10000次去判断A表中的id是否与B表中的id相等. 如:A表有10000条记录,B表有100000000条记录,那么exists()还是执行10000次,因为它只执行A.length次,可见B表数据越多,越适合exists()发挥效果. 再如:A表有10000条记录,B表有100条记录,那么exists()还是执行10000次,还不如使用in()遍历10000*100次,因为in()是在内存里遍历比较,而exists()需要查询数据库,我们都知道查询数据库所消耗的性能更高,而内存比较很快.

结论:exists()适合B表比A表数据大的情况

当A表数据与B表数据一样大时,in与exists效率差不多,可任选一个使用.

区别及应用场景

in 和 exists的区别:

如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in, 反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists。其实我们区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询,所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了 ,另外IN时不对NULL进行处理。

in 是把外表和内表作hash 连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询。一直以来认为exists比in效率高的说法是不准确的。

更多细节,可以参考以下博客(SQL语句中exists和in的区别),因为我也是看了这个博客写的文章。

sql中in和exists的原理及使用场景。的更多相关文章

  1. 关于sql中in 和 exists 的效率问题,in真的效率低吗

    原文: http://www.cnblogs.com/AdamLee/p/5054674.html 在网上看到很多关于sql中使用in效率低的问题,于是自己做了测试来验证是否是众人说的那样. 群众: ...

  2. SQL中如何使用EXISTS替代IN

    原创作品,可以转载,但是请标注出处地址http://www.cnblogs.com/V1haoge/p/6385312.html 我们在程序中一般在做SQL优化的时候讲究使用EXISTS带替代IN的做 ...

  3. 关于sql中in 和 exists 的效率问题

    在用in的地方可以使用freemark标签代替,例如: 将 <#if assistantList??&& (assistantList?size > 0)> AND ...

  4. 问题:PLS-00204: 函数或伪列 'EXISTS' 只能在 SQL 语句中使用;结果:PL/SQL中不能用exists函数?

    怎么写了一个语句带出这样的结果. 语句: if exists (select * from sysdatabases where name='omni') then 结果: ERROR 位于第 4 行 ...

  5. sql中in和exists效率问题 转自百度知道

    in和existsin 是把外表和内表作hash 连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询. 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询 ...

  6. SQL中 left join 的底层原理

    介绍 left join的实现效果就是保留左表的全部信息,将右表往左表上拼接,如果拼不上则为NULL. 除了left join以外,还有inner join.outer join.right join ...

  7. SQL中IN与EXISTS的区别

    1.IN子句中的子查询只能返回一个字段,不允许返回多个字段,而EXISTS可以返回多个字段 2.IN返回的是某字段的值,而EXISTS返回的则是True或False,EXISTS子句存在符合条件的结果 ...

  8. SQL中IN和EXISTS用法的区别

    结论 1. in()适合B表比A表数据小的情况 2. exists()适合B表比A表数据大的情况 当A表数据与B表数据一样大时,in与exists效率差不多,可任选一个使用. select * fro ...

  9. sql中in和exists的区别

    in 和exists in是把外表和内表作hash 连接,而exists 是对外表作loop 循环,每次loop 循环再对内表进行查询. 一直以来认为exists 比in 效率高的说法是不准确的.如果 ...

随机推荐

  1. ESP8266局域网 路由器下作服务器模式串口透传 arduino uno示例 模板参考2

    ESP8266服务器模式串口透传编译需要下载8266的库文件后才可以正常 准备工作 下载一个Arduino IDE,下载8266的库文件ESP8266服务器模式串口透传编译 功能说明 1.直接使用路由 ...

  2. 用VC++6.0,双击主对话框中的按钮时,不能跳转到代码处

    1. 首先在项目中--[生成]build--[清除解决方案]clean 2. 关闭项目 3. 删除项目中的[Debug]下所有文件 4. 把*.aps,*.clw,*.ncb,*.opt删掉----- ...

  3. MFC vc++严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C3646 “m_SockClient”: 未知重写说明符

    严重性 代码 说明 项目 文件 行 禁止显示状态错误 C3646 “m_SockClient”: 未知重写说明符 MFC_TCP_CSocket_Client c:\users\tt2018\docu ...

  4. NodeJs将异步方法改为同步以上传文件为例

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 下面这个例子既写 ...

  5. 二叉查找树、平衡二叉树(AVLTree)、平衡多路查找树(B-Tree),B+树

    B+树索引是B+树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引. B+树中的B代表平衡(balance),而不是二叉(binary),因为B+树是从最早的平衡二叉树演化而来的. 在 ...

  6. java 基本类型详解 及 常见问题

    鄙人不才,基础不好,趁着闲时简单学习一下,仅作学习分享,如有不正确地方还请各位看客不吝指出. 常用的基本类型有:byte(8).short(16).char(16,取值从0-65535[2^16-1] ...

  7. 【DP-动态代理】JDK&Cglib

    需求:增强未知方法的代码 简单方案:继承或者聚合 继承,调用方法前后加增强逻辑 聚合 - 静态代理 持有被代理类对象 或者接口 可通过嵌套实现代理的组合 和 装饰器模式很像 高级方案 代理所有的类,不 ...

  8. 使用反应式关系数据库连接规范R2DBC操作MySQL数据库

    1. 简介 三月份已经介绍过R2DBC,它是一种异步的.非阻塞的关系式数据库连接规范.尽管一些NoSQL数据库供应商为其数据库提供了反应式数据库客户端,但对于大多数项目而言,迁移到NoSQL并不是一个 ...

  9. Python实用笔记 (12)函数式编程——高阶函数

    函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数! Python对函数式编程提供部分支持.由于Python允许使用变量,因此,Python不是纯函数式编程语言. 变量 ...

  10. jquery 获取页面和滚动条的高度

    1.获取浏览器显示区域的高度 : $(window).height(); 2.获取浏览器显示区域的宽度 : $(window).width(); 3.获取页面的文档高度 : $(document).h ...