(网页)在SQL Server中为什么不建议使用Not In子查询(转)
转自博客园宋沄剑 英文名:CareySon :
在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题:
- 结果不准确
- 查询性能低下
下面我们来看一下为什么尽量不使用Not In子句。
结果不准确问题
在SQL Server中,Null值并不是一个值,而是表示特定含义,其所表示的含义是“Unknow”,可以理解为未定义或者未知,因此任何与Null值进行比对的二元操作符结果一定为Null,包括Null值本身。而在SQL Server中,Null值的含义转换为Bool类型的结果为False。让我们来看一个简单的例子,如图1所示。

图1.Null值与任何值进行对比结果都为Null
SQL Server提供了“IS”操作符与Null值做对比,用于衡量某个值是否为Null。
那么Not In 的问题在哪呢,如图2所示。

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

图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所示。

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

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

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

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

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

图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所示。

图10.Not In吃掉很高的IO
本文阐述了Not In 的实现原理以及所带来的数据不一致和性能问题,在写查询时,尽量避免使用Not In,而转换为本文提供的Not Exists等价形式,将会减少很多麻烦。
原文:http://www.cnblogs.com/CareySon/p/4955123.html
(网页)在SQL Server中为什么不建议使用Not In子查询(转)的更多相关文章
- 在SQL Server中为什么不建议使用Not In子查询
在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题: 结果不准确 查询性能低下 下面 ...
- SQL SERVER 中 实现主表1行记录,子表多行记录 整合成一条虚拟列
表中有这样的记录,简单的主子表,现要想通过left join 语句把两表关联起来 select * from tbl_diary_reback a left join tbl_diary_reback ...
- 《SQL Server 2012 T-SQL基础》读书笔记 - 4.子查询
Chapter 4 Subqueries 子查询分为:独立子查询(Self-Contained Subqueries)和相关子查询(Correlated Subqueries),独立子查询可以单独拿出 ...
- SQL Server中INNER JOIN与子查询IN的性能测试
这个月碰到几个人问我关于"SQL SERVER中INNER JOIN 与 IN两种写法的性能孰优孰劣?"这个问题.其实这个概括起来就是SQL Server中INNER JOIN与子 ...
- SQL Server中查询数据库及表的信息语句
/* -- 本文件主要是汇总了 Microsoft SQL Server 中有关数据库与表的相关信息查询语句. -- 下面的查询语句中一般给出两种查询方法, -- A方法访问系统表,适应于SQL 20 ...
- 多个程序对sql server中的表进行查询和插入操作导致死锁
最近在做一个项目,是要用多个程序对sql server中的相同的数据库进行操作(查询和插入),所以在开始的时候常会出现死锁问题,后来在网上进行了咨询,发现了一些解决方法,留作大家参考: 并发去操纵一张 ...
- SQL Server中的“最大并行度”的配置建议
SQL Server中的最大并行度(max degree of parallelism)如何设置呢? 设置max degree of parallelism有什么好的建议和指导方针呢?在微软官方文档R ...
- 在SQL Server中,为何都建议禁止 VIA 协议,VIA协议具体内容是什么?
在SQL Server 在SQL Server中,为何都建议禁止 VIA 协议,VIA协议具体内容是什么? 中,为何都建议禁止 VIA 协议,VIA协议具体内容是什么? 在SQL Server中,为何 ...
- SQL Server中的高可用性(2)----文件与文件组
在谈到SQL Server的高可用性之前,我们首先要谈一谈单实例的高可用性.在单实例的高可用性中,不可忽略的就是文件和文件组的高可用性.SQL Server允许在某些文件损坏或离线的情况下,允 ...
随机推荐
- Win10手记-为应用集成SQLite(二)
接上篇内容,这里给大家分享我的辅助访问类,采用了异步方法,封装了常用的访问操作,一些操作还是纯CLI的. SQLiteDBManager using System; using System.Coll ...
- AB(ApacheBench)工具 -- 压力测试
服务器负载太大而影响程序效率也是很常见的,Apache服务器自带有一个叫AB(ApacheBench)的工具,可以对服务器进行负载测试 同时美多商城的秒杀功能也会被高负载影响,从而导致超卖现象 安装x ...
- vue项目中在同一页面多次引入同一个echarts图表的自适应问题
在父组件页面引入两次该图表子组件显示的效果: 由于是百分比宽高,所以在窗口发生变化时,需要让图表也跟着自适应,所以才出现了本次讨论的问题啦. 先看下完整的图表子组件代码(在父组件就是直接引入,不需要传 ...
- 安装mysql.zip文件教程(包含常见问题修复)
参考链接: https://jingyan.baidu.com/article/63f236284aca280208ab3dcc.html https://jingyan.baidu.com/arti ...
- HDFS-Architecture剖析
1.概述 从HDFS的应用层面来看,我们可以非常容易的使用其API来操作HDFS,实现目录的创建.删除,文件的上传下载.删除.追加(Hadoop2.x版本以后开始支持)等功能.然而仅仅局限与代码层面是 ...
- visual studio code (vsc)中查看 php 数组的全部元素
在 vsc 调试 php 时,如果数组元素过多,只能查看前面 30个左右的元素,如果需要看更多的元素,可以配置 xDebugSettings 修改项目中的 .vscode/launch.json 文件 ...
- C语言第十二讲,文件操作.
C语言第十二讲,文件操作. 一丶文件操作概述 在操作系统中,我们的文档都称为文件.操作系统也为我们提供了接口进行操作.不同语言都是使用的相同的接口,只不过封装的上层接口不一样 操作文件的步骤 打开文件 ...
- 120分钟React快速扫盲教程
在教程开端先说些题外话,我喜欢在学习一门新技术或读过一本书后,写一篇教程或总结,既能帮助消化,也能加深印象和发现自己未注意的细节,写的过程其实仍然是一个学习的过程.有个记录的话,在未来需要用到相关知识 ...
- 2017年最新20个轻量的 JavaScript 库和插件
下面这个列表中的免费 JavaScript 插件都是今年发布的,没有臃肿的一体化的框架,它们提供轻量级的解决方案,帮助 Web 开发过程更容易和更快.提供的插件可以创建滑块.响应式菜单.模态窗口.相册 ...
- PHP 5.3中的命名空间使用方法
PHP的命名空间(namespace)是php5.3之后才有的.这个概念在C#中已经很早就有了,php中的namespace其实和c#的概念是一样的. 为什么php中要使用namespace? 假设如 ...