转自博客园宋沄剑  英文名: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子查询(转)的更多相关文章

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

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

  2. SQL SERVER 中 实现主表1行记录,子表多行记录 整合成一条虚拟列

    表中有这样的记录,简单的主子表,现要想通过left join 语句把两表关联起来 select * from tbl_diary_reback a left join tbl_diary_reback ...

  3. 《SQL Server 2012 T-SQL基础》读书笔记 - 4.子查询

    Chapter 4 Subqueries 子查询分为:独立子查询(Self-Contained Subqueries)和相关子查询(Correlated Subqueries),独立子查询可以单独拿出 ...

  4. SQL Server中INNER JOIN与子查询IN的性能测试

    这个月碰到几个人问我关于"SQL SERVER中INNER JOIN 与 IN两种写法的性能孰优孰劣?"这个问题.其实这个概括起来就是SQL Server中INNER JOIN与子 ...

  5. SQL Server中查询数据库及表的信息语句

    /* -- 本文件主要是汇总了 Microsoft SQL Server 中有关数据库与表的相关信息查询语句. -- 下面的查询语句中一般给出两种查询方法, -- A方法访问系统表,适应于SQL 20 ...

  6. 多个程序对sql server中的表进行查询和插入操作导致死锁

    最近在做一个项目,是要用多个程序对sql server中的相同的数据库进行操作(查询和插入),所以在开始的时候常会出现死锁问题,后来在网上进行了咨询,发现了一些解决方法,留作大家参考: 并发去操纵一张 ...

  7. SQL Server中的“最大并行度”的配置建议

    SQL Server中的最大并行度(max degree of parallelism)如何设置呢? 设置max degree of parallelism有什么好的建议和指导方针呢?在微软官方文档R ...

  8. 在SQL Server中,为何都建议禁止 VIA 协议,VIA协议具体内容是什么?

    在SQL Server 在SQL Server中,为何都建议禁止 VIA 协议,VIA协议具体内容是什么? 中,为何都建议禁止 VIA 协议,VIA协议具体内容是什么? 在SQL Server中,为何 ...

  9. SQL Server中的高可用性(2)----文件与文件组

        在谈到SQL Server的高可用性之前,我们首先要谈一谈单实例的高可用性.在单实例的高可用性中,不可忽略的就是文件和文件组的高可用性.SQL Server允许在某些文件损坏或离线的情况下,允 ...

随机推荐

  1. centos7的ssh服务连接

    ---恢复内容开始--- SSH 为 Secure Shell 的缩写,由 IETF 的网络小组(Network Working Group)所制定:SSH 为建立在应用层基础上的安全协议.SSH 是 ...

  2. Hive SQL基础操作

    创建表 hive 查看本地的文件#Can execute local commands within CLI, place a command in between ! and ;!cat data/ ...

  3. spring boot 集成freemarker

  4. asp.net mvc开发过程中的一些小细节

    现在做网站用mvc越来越普及了,其好处就不说了,在这里只记录一些很多人都容易忽视的地方. 引用本地css和js文件的写法 这应该是最不受重视的地方,有同事也说我有点小题大作,但我觉得用mvc还是得有一 ...

  5. 21天打造分布式爬虫-Spider类爬取糗事百科(七)

    7.1.糗事百科 安装 pip install pypiwin32 pip install Twisted-18.7.0-cp36-cp36m-win_amd64.whl pip install sc ...

  6. Android的Fragment中的互相通信-桥梁activity

    Android的Fragment中的互相通信-桥梁activity 效果图如下: 项目结构图如下: Fragment1: package com.demo.fragmenttongxin; impor ...

  7. jar包版本介绍(beta,alpha,release),软件的版本介绍

    α(Alpha) 此版本表示该软件仅仅是一个初步完成品,通常只在软件开发者内部交流,也有很少一部分发布给专业测试人员.一般而言,该版本软件的bug(漏洞)较多,普通用户最好不要安装.主要是开发者自己对 ...

  8. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十六):容器部署项目

    容器部署项目 这一章我们引入docker,采用docker容器的方式部署我们的项目. 首先需要有一个linux环境,并且安装 java 和 maven 以及 docker 环境,这个教程多如牛毛,不再 ...

  9. javascript变量提升详解

    js变量提升 对于大多数js开发者来说,变量提升可以说是一个非常常见的问题,但是可能很多人对其不是特别的了解.所以在此,我想来讲一讲. 先从一个简单的例子来入门: a = 2; var a; cons ...

  10. memcached优化方案实例

    <?php //引入memcached require_once '../class/memcached.class.php'; //连接MySQL $link = mysqli_connect ...