一文浅谈sql中的 in与not in,exists与not exists的区别以及性能分析

news2024/11/13 14:31:04

文章目录

  • 1. 文章引言
  • 2. 查询对比
    • 2.1 in和exists
    • 2.2 not in 和not exists
    • 2.3 in 与 = 的区别
  • 3. 性能分析
    • 3.1 in和exists
    • 3.2 NOT IN 与NOT EXISTS
  • 4. 重要总结

1. 文章引言

我们在工作的过程中,经常使用in,not in,exists,not exists来查询,比如现在一张项目(project)表,表的结构和数据:

CREATE TABLE `project` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `status` varchar(255) DEFAULT NULL,
  `project_name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

INSERT INTO `project` VALUES ('1', 'finish', '太湖佳园');
INSERT INTO `project` VALUES ('2', 'during', '尚东雅园');
INSERT INTO `project` VALUES ('3', 'start', '水乡苑一区');
INSERT INTO `project` VALUES ('4', 'during', '水乡苑二区');

查询状态为已完成和进行中的记录,我们可以写成如下的SQL语句:

select * from project where `status` in ('finish','during');

查询结果如下图:

在这里插入图片描述

这只是我们开发中的一个简单示例,接下来,我们详细解说 in与not in,exists与not exists的区别以及性能分析

2. 查询对比

2.1 in和exists

in是把外表和内表作hash连接。

exists是对外表作loop循环,每次loop循环再对内表进行查询,一直以来认为existsin效率高的说法是不准确的。

如果查询的两个表大小相当,那么用inexists差别不大。

如果两个表中一个较小一个较大,则子查询表大的用exists,子查询表小的用in

例如:表A(小表),表B(大表)


-- 效率低,用到了A表上cc列的索引
select * from A where cc in(select cc from B)  

-- 效率高,用到了B表上cc列的索引
select * from A where exists(select cc from B where cc=A.cc) 

相反的:


-- 效率高,用到了B表上cc列的索引
select * from B where cc in(select cc from A) 

-- 效率低,用到了A表上cc列的索引。
select * from B where exists(select cc from A where cc=B.cc)  

2.2 not in 和not exists

not in逻辑上不完全等同于not exists,如果你误用了not in小心你的程序存在致命的BUG,请看下面的例子:

-- 创建t1表
create table t_1(c1 int,c2 int);

-- 创建t2表
create table t_2(c1 int,c2 int);

-- 向t1表中插入数据
insert into t_1 values(1,2);
insert into t_1 values(1,3);

-- 向t2表中插入数据
insert into t_2 values(1,2);
insert into t_2 values(1,null); 

先后执行如下两条查询语句:

  1. 语句1
SELECT
	*
FROM
	t_1
WHERE
	c2 NOT IN (SELECT c2 FROM t_2);

查询结果是空值,如下图:

在这里插入图片描述

  1. 语句2
SELECT
	*
FROM
	t_1
WHERE
	NOT EXISTS (
		SELECT
			1
		FROM
			t_2
		WHERE
			t_2.c2 = t_1.c2
	);

查询结果c1 = 1,c2 = 3,如下图所示:

在这里插入图片描述

正如你所看到的,not in出现了不期望的结果集,存在逻辑错误。

如果看一下上述两个select语句的执行计划,也会不同,语句2使用了hash_aj,所以,请尽量不要使用not in(它会调用子查询),而尽量使用not exists(它会调用关联子查询)。

如果子查询中返回的任意一条记录含有空值,则查询将不返回任何记录。

如果子查询字段有非空限制,这时可以使用not in,并且可以通过提示让它用hasg_ajmerge_aj连接。

如果查询语句使用了not in,那么对内外表都进行全表扫描,没有用到索引。而not exists的子查询依然能用到表上的索引。所以无论哪个表大,用not exists都比not in 要快。

2.3 in 与 = 的区别


SELECT
	NAME
FROM
	student
WHERE
	NAME IN ('zhang', 'wang', 'zhao');

SELECT
	NAME
FROM
	student
WHERE
	NAME = 'zhang'
OR NAME = 'wang'
OR NAME = 'zhao'

的结果是相同的。

3. 性能分析

3.1 in和exists

  1. EXISTS的执行流程
SELECT
	*
FROM
	t1
WHERE
	EXISTS (SELECT NULL FROM t2 WHERE y = x)

可以理解为:

for x in ( select * from t1 ) loop 

if ( exists ( select null from t2 where y = x.x ) then 
OUTPUT THE RECORD 
end if 
end loop 
  1. inexists的性能区别

如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in

反之,如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists

其实我们区分inexists主要是造成了驱动顺序的改变(这是性能变化的关键):

  • 如果是exists,那么以外层表为驱动表,先被访问

  • 如果是IN,那么先执行子查询

所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了 。

另外,IN时不对NULL进行处理,如下SQL所示:

SELECT
	1
FROM
	DUAL
WHERE
	NULL IN (0, 1, 2, NULL)

查询结果为空。

3.2 NOT IN 与NOT EXISTS

  1. NOT EXISTS的执行流程
SELECT
	.....
FROM
	ROLLUP R
WHERE
	NOT EXISTS (
		SELECT
			'Found'
		FROM
			title T
		WHERE
			R.source_id = T.Title_ID
	);

可以理解为:

for x in ( select * from rollup ) loop 
if ( not exists ( that query ) ) then 
OUTPUT 
end if; 
end loop; 

注意:NOT EXISTSNOT IN不能完全互相替换,看具体的需求。如果选择的列可以为空,则不能被替换。

例如下面语句,看他们的区别:

select x,y from t; 

查询xy数据如下所示:

x      y 
------ ------ 
1      3 
3      1 
1      2 
1      1 
3      1 
5 
  1. 使用not innot exists查询结果,如下
SELECT
	*
FROM
	t
WHERE
	x NOT IN (SELECT y FROM t t2);

查询无结果:no rows

SELECT
	*
FROM
	t
WHERE
	NOT EXISTS (
		SELECT
			NULL
		FROM
			t t2
		WHERE
			t2.y = t.x
	);

查询结果为:

x       y 
------ ------ 
5      NULL 

所以要具体需求来决定

  1. not innot exists的性能区别

not in只有当子查询中,select关键字后的字段有not null约束,或者有这种暗示时用not in。另外,如果主查询中表大,子查询中的表小但是记录多,则应当使用not in,并使用anti hash join

如果主查询表中记录少,子查询表中记录多,并有索引,可以使用not exists,另外,not in最好也可以用/*+ HASH_AJ */或者外连接+is null

NOT IN在基于成本的应用中较好,比如:

SELECT
	.....
FROM
	ROLLUP R
WHERE
	NOT EXISTS (
		SELECT
			'Found'
		FROM
			title T
		WHERE
			R.source_id = T.Title_ID
	);

最好修改成如下方式:

SELECT
	......
FROM
	title T,
	ROLLUP R
WHERE
	R.source_id = T.Title_id (+)
AND T.Title_id IS NULL;

或者(佳):

SELECT
	/*+ HASH_AJ */...
FROM
	ROLLUP R
WHERE
	ource_id NOT IN (
		SELECT
			ource_id
		FROM
			title T
		WHERE
			ource_id IS NOT NULL
	)

4. 重要总结

讨论INEXISTS

select * from t1 where x in ( select y from t2 ) 

事实上可以理解为:

SELECT
	*
FROM
	t1,
	(SELECT DISTINCT y FROM t2) t2
WHERE
	t1.x = t2.y;

如果你有一定的SQL优化经验,从这句很自然的可以想到t2绝对不能是个大表,因为需要对t2进行全表的唯一排序

如果t2很大,这个排序的性能是不可忍受的,但是t1可以很大,为什么呢?

最通俗的理解就是因为t1.x=t2.y可以走索引。但这并不是一个很好的解释。

试想,如果t1.xt2.y都有索引,我们知道索引是种有序的结构,因此t1t2之间最佳的方案是走merge join

另外,如果t2.y上有索引,对t2的排序性能也有很大提高。

select * from t1 where exists ( select null from t2 where y = x ) 

可以理解为:

for x in ( select * from t1 ) 
loop 
if ( exists ( select null from t2 where y = x.x ) 
then 
OUTPUT THE RECORD! 
end if 
end loop 

这个更容易理解,t1永远是个表扫描!因此t1绝对不能是个大表,而t2可以很大,因为y=x.x可以走t2.y的索引。

综合以上对IN/EXISTS的讨论,我们可以得出一个基本通用的结论:

  1. IN适合于外表大而内表小的情况;
  2. EXISTS适合于外表小而内表大的情况。

我们要根据实际的情况做相应的优化,不能绝对的说谁的效率高谁的效率低,所有的事都是相对的

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

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

相关文章

Unity2018.4.x~2021.3.x版 Android资源处理

注意:本文都是针对使用Gradle编译从Unity2018.4.x到Unity2020.3.x都是可以直接将Android的适配资源直接放到${PROJECT_PATH}/Assets/Plugins/Android/对应的目录下的,如:在此目录下可以方安卓平台对应的assets、res目录及子目录资源&#xff…

前端的CSS样式表知识提要

文章目录前言基本概念屏幕尺寸屏幕分辨率屏幕像素密度/像素密度/屏幕密度视口和浏览器窗口长度单位:px、em/rem和vhCSS属性的继承与覆盖CSS选择器CSS 布局基础盒子模型绝对定位和相对定位display属性浮动正常布局流(normal flow)Flexbox 布局…

【Python表白代码】 2.14“Valentine‘s Day”“没别的意思 就是借着特殊日子说声喜欢你”你在哪儿?我去见你~(各种玫瑰源码合集)

导语 Valentines Day Every man is a poet when he is in love 所有文章完整的素材源码都在👇👇 粉丝白嫖源码福利,请移步至CSDN社区或文末公众hao即可免费。 哈喽!我是你们的木木子吖~ 情人节又到了,礼物备好了没&am…

k8s部署Prometheus+Grafana

1.prometheus简介 Prometheus是一个开源的系统监控和警报工具包,最初由SoundCloud开发的,社区活跃,2016年加入了云原生计算基金会成为继Kubernetes之后的第二个托管项目;普罗米修斯以时间序列数据的形式收集并存储度量值&#xff…

python爬虫--xpath模块简介

一、前言 前两篇博客讲解了爬虫解析网页数据的两种常用方法,re正则表达解析和beautifulsoup标签解析,所以今天的博客将围绕另外一种数据解析方法,它就是xpath模块解析,话不多说,进入内容: 一、简介 XPat…

2023年谷歌蜘蛛池最全指南

本文主要是2023年关于谷歌蜘蛛池的一系列疑问,我们逐一提供解答。 本文由光算创作,有可能会被修改和剽窃,我们佛系对待这种行为吧。 首先最常见的新手问题是“什么叫谷歌蜘蛛池?” 答案是:谷歌蜘蛛池是一个深度研究谷…

微服务网关(九)负载均衡底层详细

微服务网关(九)负载均衡 四大负载均衡策略 随机负载 随机挑选目标服务器IP 轮询负载 ABC三台服务器,以ABCABC的顺序依次轮询 加权轮询 给目标服务器设置访问权重值,按照权重轮询负载 一致性哈希轮询 固定的一个客户端IP请求访…

图文解答之最短路径||

最短路径|| 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 现在考虑网格中有障碍物。那么从左上角到…

rust过程宏 proc-macro-workshop解题-0-介绍

名字版本号rust1.69.0OSubuntu22.04proc-macro-workshop是一个学习rust过程宏非常好的工程。里边包含五大类题目。并且每种题目都有实际价值,可以应用在企业级项目中。我们在这里先介绍一下这个项目如何运行如何测试,如何验证过程宏的正确性以及如何调试。 本文只围绕以下几个…

2月编程语言排行榜出炉,第一名势头强劲

近日,TIOBE公布了2023年2月编程语言排行榜,本月各个语言表现如何?谁又摘得桂冠? TIOBE 2月Top15编程语言: 详细榜单查看TIOBE官网 https://www.tiobe.com/tiobe-index/ 关注IT行业的小伙伴们都知道,编程…

Linux入门篇(一)

Linux前言Linux初探Linux内核GNU实用工具shellLinux发行版bash shell 基础Linux文件系统Linux文件操作命令前言 在阅读诸如docker之类的书的时候,经常碰到Linux的知识。同时,大部分的盲区也是在Linux方面。因此就想稍微了解一下这个广为人使用的操作系统…

docker-入门到精通

docker知识总结 参考文档 https://jiajially.gitbooks.io/dockerguide/content/chapter_fastlearn/docker_run/–volumes-from.html 1、什么是docker ​ 容器技术、虚拟化技术已经成为一种被大家广泛认可的服务器资源共享方式,容器技术可以在按需构建操作系统实例…

vue3 Proxy响应式原理分析(面试题)

在开始正文前,先理一下vue2 Object.defineProperty 和 vue 3 Proxy 区别: Object.defineProperty:数据劫持 Proxy:数据代理 注意: 响应式原理和双向数据绑定原理是两回事,一般面试官会先问响应式原理再问双…

内网渗透(二十三)之Windows协议认证和密码抓取-Mimikatz介绍和各种模块使用方法

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

ShardingSphere-Proxy 数据库协议交互解读

数据库协议对于大部分开发者来说算是比较冷门的知识,一般的用户、开发者都是通过现成的数据库客户端、驱动使用数据库,不会直接操作数据库协议。不过,对数据库协议的特点与流程有一些基本的了解,有助于开发者在排查数据库功能、性…

Fabric磁盘扩容后数据迁移

线上环境原来的磁盘比较小,随着业务数据的增多,磁盘需要扩容,因此需要把原来docker数据转移至新的数据盘。 数据迁移 操作系统: centOS 7   docker默认的数据目录为/var/lib/docker   创建一个新的目录/opt/dockerdata&…

Halcon转OpenCV实例--OCR字符识别(附源码)

导 读 本文主要介绍Halcon转OpenCV实例--OCR字符识别(附源码)。 实例来源 实例来源于51Halcon论坛的帖子,原贴地址: https://www.51halcon.com/forum.php?mod=viewthread&tid=889 Halcon实现 测试图: 实现代码与效果: read_image (Image, ET.png)decompose3…

智慧园区解决方案

智慧园区解决方案 智慧园区是以互联网为载体,“互联网产业”融合产业模式为手段,面向园区提供全产业链支撑服务的解决方案。能够帮助园区在信息化方面建立统一的组织管理协调架构,业务管理平台和对内对外服务运营平台。将相关资源形成紧密联…

Java最新学习路线

Java语言是目前流行的互联网等企业的开发语言,是市面上很多程序员喜欢并且在用的程序设计语言。关于学习java,有一部分人是为了就业或自己创业,而大多数人是希望使用java这个开发语言用来工作,开发出计算机后端系统,利…

python带你采集回车桌面高清写真壁纸

前言 大家早好、午好、晚好吖 ❤ ~ 壁纸嘛~大家都在用,每个人喜欢的壁纸类型也不同 那今天来教大家怎么批量保存一批高质量壁纸~ 开发环境: Python 3.8 Pycharm 模块使用: requests >>> pip install requests 数据请求 parsel >>> pip instal…