在SQL Server中为什么不建议使用Not In子查询

原文:在SQL Server中为什么不建议使用Not In子查询

    在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题:

  • 结果不准确
  • 查询性能低下

 

    下面我们来看一下为什么尽量不使用Not In子句。

 

结果不准确问题

    在SQL Server中,Null值并不是一个值,而是表示特定含义,其所表示的含义是“Unknow”,可以理解为未定义或者未知,因此任何与Null值进行比对的二元操作符结果一定为Null,包括Null值本身。而在SQL Server中,Null值的含义转换为Bool类型的结果为False。让我们来看一个简单的例子,如图1所示。

image

图1.Null值与任何值进行对比结果都为Null

    SQL Server提供了“IS”操作符与Null值做对比,用于衡量某个值是否为Null。

 

    那么Not In 的问题在哪呢,如图2所示。

image   

图2.Not In产生不准确的值

 

     在图2中,条件3不属于Not In后面列表的任意一个,该查询却不返回任何值,与预期的结果不同,那么具体原因就是Not In子句对于Null值的处理,在SQL Server中,图2中所示的Not In子句其实可以等价转换为如图3所示的查询。

image

图3.对于Not In子句来说,可以进行等价转换

 

    在图3中可以看到Not In可以转换为条件对于每个值进行不等比对,并用逻辑与连接起来,而前面提到过Null值与任意其他值做比较时,结果永远为Null,在Where条件中也就是False,因此3<>null就会导致不返回任何行,导致Not In子句产生的结果在意料之外。

    因此,Not In子句如果来自于某个表或者列表很长,其中大量值中即使存在一个Null值,也会导致最终结果不会返回任何数据。

解决办法?

    解决办法就是不使用Not In,而使用Not Exists作为替代。Exists的操作符不会返回Null,只会根据子查询中的每一行决定返回True或者False,当遇到Null值时,只会返回False,而不会由某个Null值导致整个子查询表达式为Null。对于图2中所示的查询,我们可以改写为子查询,如图4所示。

image

图4.Not Exists可以正确返回结果

 

Not In导致的查询性能低下

    前面我们可以看出,Not In的主要问题是由于对Null值的处理问题所导致,那么对Null值的处理究竟为什么会导致性能问题?让我们来看图5的示例。图5中,我们使用了Adventurework示例数据库,并为了演示目的将SalesOrderDetail表的ProductId的定义由Not Null改为Null,此时我们进行一个简单的Not In查询。如图5所示。

image

图5.Not In的执行计划

 

    在图5中,我们看到一个Row Count Spool操作符,该操作符用于确认ProductId列中是否有Null值(过程是对比总行数和非Null行数,不想等则为有Null值,虽然我们知道该列中没有Null值,但由于列定义是允许Null的,因此SQL Server必须进行额外的确认),而该操作符占用了接近一半的查询成本。因此我们对比Not Exists,如图6所示。

image

图6.Not In Vs Not Exists

 

    由图6可以看出,Not In的执行成本几乎是Not Exists的3倍,仅仅是由于SQL Server需要确认允许Null列中是否存在Null。根据图3中Not In的等价形式,我们完全可以将Not In转换为等价的Not Exist形式,如图7所示。

image

图7.Not In转换为Not Exists

    我们来对比图7和其等价Not In查询的成本,如图8所示。

image

图8.成本上完全等价

 

    因此我们可以看到Not In需要额外的步骤处理Null值,上述情况是仅仅在SalesOrderDetail表中的ProductId列定义为允许Null,如果我们将SalesOrderHeader的SalesOrderID列也定义为允许Null时,会发现SQL Server还需要额外的成本确认该列上是否有Null值。如图9所示。

image

图9.SQL Server通过加入Left Anti Semi Join操作符解决列允许Null的问题

 

此时Not In对应的等价Not Exist形式变为如代码清单1所示。

SELECT  *
FROM    Sales.SalesOrderHeader a
WHERE   NOT EXISTS ( SELECT *
                     FROM   Sales.SalesOrderDetail b
                     WHERE  a.SalesOrderID = b.ProductID )
        AND NOT EXISTS ( ( SELECT   *
                           FROM     Sales.SalesOrderDetail b
                           WHERE    b.ProductID IS NULL
                         ) )
        AND NOT EXISTS ( SELECT 1
                         FROM   ( SELECT    *
                                  FROM      Sales.SalesOrderHeader
                                ) AS c
                         WHERE  c.SalesOrderID IS NULL )

代码清单1.当连接列两列定义都允许Null时,Not In等价的Not Exists形式

 

    此时我们简单对比Not In和Not Exists的IO情况,如图10所示。

image

图10.Not In吃掉很高的IO

 

小结

    本文阐述了Not In 的实现原理以及所带来的数据不一致和性能问题,在写查询时,尽量避免使用Not In,而转换为本文提供的Not Exists等价形式,将会减少很多麻烦。

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

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

相关文章

结组项目-四则运算3

团队成员&#xff1a;苗堃&#xff08;http://www.cnblogs.com/brucekun/p/5294368.html&#xff09;、罗毅&#xff08;http://www.cnblogs.com/ly199553/p/5294779.html&#xff09; PSP总结http://www.cnblogs.com/ly199553/p/5295545.html 本次软件工程老师提出了新任务&a…

java 的io流需要学吗_Java Io流怎么学习呢?

滕泰科技之 io 流流是一组有顺序的&#xff0c;有起点和终点的字节集合&#xff0c;是对数据传输的总称或抽象。即数据在两设备间的传输称为流&#xff0c;流的本质是数据传输&#xff0c;根据数据传输特性将流抽象为各种类&#xff0c;方便更直观的进行数据操作。根据处理数据…

mysql5.5.20安装_mysql5.5.20安装文档

1.安装cmakeMySQL从5.5版本开始&#xff0c;通过./configure进行编译配置方式已经被取消&#xff0c;取而代之的是cmake工具。因此&#xff0c;我们首先要在系统中源码编译安装cmake工具。# wget http://www.doczj.com/doc/e83fe02b7375a417866f8f0a.html/files/v2.8/cmake-2.8…

心里装着你,思念就很美(我要做一个文艺范儿的程序员)

每一个静静的夜里&#xff0c; 都会枕着你的名字悄然入睡。 睡梦里轻轻的将你想起&#xff0c; 深深的把你惦起&#xff01; 美丽的邂逅在梦里重现&#xff0c; 你我相遇在花开如诗的季节&#xff0c; 春花乱舞迷了你的眼&#xff0c; 你如花般的笑颜却迷了我的心坎。 记忆清晰…

java8新特性以及原因_JAVA8 十大新特性详解

二、Lambda 表达式首先看看在老版本的Java中是如何排列字符串的&#xff1a;List names Arrays.asList("peter", "anna", "mike", "xenia");Collections.sort(names, new Comparator() { Override public int compare(String a…

jsp 嵌入java_关于JSP里的Java语句嵌入问题

是这样的,我用的是Intellij 导入了一个MyEclipse的包(该包已部署在服务器上&#xff0c;所以是没有大错误的)。然后在一个jsp文件出现了个问题&#xff1a;莫名奇妙地println变成了红色&#xff0c;执行的时候就直接抛出异常了。今天刚接触JavaWeb不太懂&#xff0c;到底是怎么…

ecshop 快速添加会员

/*------------------------------------------------------ */ //-- 快速添加会员 /*------------------------------------------------------ */ elseif ($_REQUEST[act] quick_insert) {/* 检查权限 */admin_priv(users_manage);$str1_arr array(0,1,2,3,4,5,6,7,8,9);$s…

python循环计算函数_Python位操作、判断、循环,运算

位运算1、原码、反码和补码计算机内部使用补码来表示2、按位运算实现快速计算(1) 通过^(异或)快速交换两个整数。a^bb^aa^b(2) 通过a&(-a)快速获取a的最后为1 位置的整数。00 00 01 01 -> 5&11 11 10 11 -> -5- - -00 00 00 01-> 14、利用位运算实现整数集合一…

【three.js】库

2019独角兽企业重金招聘Python工程师标准>>> three.js 一个轻量级的webgl库&#xff0c;但是十分强大。 下载地址https://github.com/mrdoob/three.js OrbitControls.js 控制视口的平移、缩放、旋转。 GridHelper.js 生成视口的网格。 转载于:https://my.oschina.…

java split空字符_java split函数结尾空字符串被丢弃的问题

java中的split函数用于将字符串分割为字符数组是很方便的&#xff0c;但由于不是很熟悉&#xff0c;犯了错误如下&#xff1a;String strtest “1,2,”;String arry[] strtest.split(“,”);这样得到的数组元素个数只是2两个&#xff0c;为什么呢&#xff0c;最后一个”,”后…

域名发散--前端优化(三)

话说天下大势&#xff0c;分久必合&#xff0c;合久必分其实域名也是一样&#xff0c;分分合合&#xff0c; 不管是域名收敛还是域名发散&#xff0c;都有着自己独特的应用场景。目前, 在webs top 30,000 URLS 里面&#xff0c; 平均每个域名承担了50个资源的请求&#xff0c;所…

java就_GitHub - IammyselfYBX/This_is_how_Java_should_be_learned: 《Java就应该这样学》

Java就该这么学说明这里使用 centos7 的openjdk&#xff0c;不是Oracle的JDKyum search java|grep jdkyum install java-1.8.0-openjdk#set Java environmentexport JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.222.b10-1.el7_7.x86_64export CALSSPATH.:$JAVA_HOME/jre/l…

HttpHandler与HttpModule的用处与区别

HttpHandler与HttpModule的用处与区别   问题1&#xff1a;什么是HttpHandler&#xff1f; 问题2&#xff1a;什么是HttpModule&#xff1f; 问题3&#xff1a;什么时候应该使用HttpHandler什么时候使用HttpModule&#xff1f; 答案1&#xff1a;HttpHandler&#xff0c;Htt…

win7变成xp风格了怎么改回_XP退役了,如何把Win7变成XP风格

展开全部方法如下&#xff1a;【步骤62616964757a686964616fe78988e69d8331333365653265①】&#xff1a;在电脑桌面空白处&#xff0c;点击鼠标右键&#xff0c;子菜单选择点击“个性化”【步骤②】&#xff1a;直接选择“基本和高对比主题”里的“Windows经典”模式整理风格发…

org.apache.catalina.LifecycleException: Failed to

2019独角兽企业重金招聘Python工程师标准>>> 文章原创地址&#xff1a;org.apache.catalina.LifecycleException: Failed to start component今天来了个新同事&#xff0c;让他先熟悉一下我们的工程项目&#xff0c;从svn下载下来以后&#xff0c;配置完成却怎么都无…

ef mysql 读写分离_EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离~终结~配置的优化和事务里读写的统一...

/// ///SQL命令拦截器///主要实现EF的读写分离/// public classCommandInterceptor : DbCommandInterceptor{staticCommandInterceptor(){readConnListDistributedReadWriteManager.Instance;sysTimer.Enabled true;sysTimer.ElapsedsysTimer_Elapsed;sysTimer.Start();}/// //…

最近的日子,很惬意!

最近的日子&#xff0c;很惬意&#xff1b;没事就去实验室看看编程&#xff0c;最近开始学习JAVA了&#xff0c;上手很快&#xff0c;有了之前面对对象C的基础&#xff0c;java也觉得不是很难&#xff0c;嗯&#xff0c;加油&#xff0c;有实验室一群优秀的人在我身边&#xff…

fastdfs java token_fastdfs 开启 token 防盗链

环境&#xff1a;fastdfsnginx ngx_fastdfs_modulephpfastdfs_php扩展开启token令牌&#xff1a;vi /etc/fdfs/http.conf#开启http.anti_steal.check_tokentrue#密钥http.anti_steal.secret_key123456重启nginxphp中&#xff0c;通过fastdfs_http_gen_token()函数生成$token,例…

objective-C CollectionView 加深(添加注册头部View)

2019独角兽企业重金招聘Python工程师标准>>> #pragma marc 添加Collection -(void)addConllectionView{//collection的布局方案UICollectionViewFlowLayout *collectionViewLayout[[UICollectionViewFlowLayout alloc]init];//设置位置大小以及布局方案_myCollectio…

java如何实现乌龟爬行_请教:一只乌龟的运动

import java.awt.*;import java.awt.event.*;import java.util.EventListener;/*打算你按上&#xff0c;乌龟上爬&#xff0c;按下&#xff0c;乌龟下爬。但是一直没有repaint&#xff0c;怎么回事呀&#xff0c;请教下什么地方错了&#xff0c;怎么才能动。*/public class WuG…