原文:开发随笔——NOT IN vs NOT EXISTS

原文出处: http://blog.csdn.net/dba_huangzj/article/details/31374037  转载请引用

之前在论坛中见到一个针对in/exists的讨论,原帖懒得找了,这里介绍一下最近的学习小结:

NOT IN和NOT EIXTS在对允许为null的列查询时会有一定的风险。特别是NOT IN,如果子查询包含了最少一个NULL,会出现非预期的结果。下面做一个演示。

IF OBJECT_ID('ShipmentItems', 'U') IS NOT NULL
DROP TABLE dbo.ShipmentItems;
GO
CREATE TABLE dbo.ShipmentItems
(
ShipmentBarcode VARCHAR(30) NOT NULL ,
Description VARCHAR(100) NULL ,
Barcode VARCHAR(30) NOT NULL
);
GO
INSERT INTO dbo.ShipmentItems
( ShipmentBarcode ,
Barcode ,
Description
)
SELECT '123456' ,
'1010203' ,
'Some cool widget'
UNION ALL
SELECT '123654' ,
'1010203' ,
'Some cool widget'
UNION ALL
SELECT '123654' ,
'1010204' ,
'Some cool stuff for some gadget';
GO
-- retrieve all the items from shipment 123654
-- that are not shipped in shipment 123456
SELECT Barcode
FROM dbo.ShipmentItems
WHERE ShipmentBarcode = '123654'
AND Barcode NOT IN ( SELECT Barcode
FROM dbo.ShipmentItems
WHERE ShipmentBarcode = '123456' );
/*
Barcode
------------------------------
1010204
*/

可以看出得到了期待结果。下面看看修改表结构,允许列为null的情况:

ALTER TABLE dbo.ShipmentItems
ALTER COLUMN Barcode VARCHAR(30) NULL;
INSERT INTO dbo.ShipmentItems
( ShipmentBarcode ,
Barcode ,
Description
)
SELECT '123456' ,
NULL ,
'Users manual for some gadget';
GO
SELECT Barcode
FROM dbo.ShipmentItems
WHERE ShipmentBarcode = '123654'
AND Barcode NOT IN ( SELECT Barcode
FROM dbo.ShipmentItems
WHERE ShipmentBarcode = '123456' );
/*
Barcode
------------------------------
*/

很多人会觉得这是一个bug,有时候能查出数据,有时候却不能。但是实际上不是bug,当NOT IN子句返回最少一个NULL时,查询会返回空,下面的语句能更好地说明这个想法:

SELECT  CASE WHEN 1 NOT IN ( 2, 3 ) THEN 'True'
ELSE 'Unknown or False'
END ,
CASE WHEN 1 NOT IN ( 2, 3, NULL ) THEN 'True'
ELSE 'Unknown or False'
END;
/*
---- ----------------
True Unknown or False
*/

实际上,由于IN的本质是OR操作,所以:

SELECT  CASE WHEN 1 IN ( 1, 2, NULL ) THEN 'True'
ELSE 'Unknown or False'
END ;

中,1 in 1,也就是为TRUE,所以返回true,这个语句的逻辑实际上是:

SELECT  CASE WHEN ( 1 = 1 )
OR ( 1 = 2 )
OR ( 1 = NULL ) THEN 'True'
ELSE 'Unknown or False'
END ;

当使用NOT IN 时,如下面的语句:

SELECT  CASE WHEN 1 NOT IN ( 1, 2, NULL ) THEN 'True'
ELSE 'Unknown or False'
END ;

会转变成:

SELECT  CASE WHEN NOT ( ( 1 = 1 )
OR ( 1 = 2 )
OR ( 1 = NULL )
) THEN 'True'
ELSE 'Unknown or False' END ;

根据离散数学的概念,可以转换为:

SELECT  CASE WHEN ( ( 1 <> 1 )
AND ( 1 <> 2 )
AND ( 1 <> NULL )
) THEN 'True'
ELSE 'Unknown or False'
END ;

谓词有短路特性,即在AND条件中,只要有一个条件为false,整个条件都为false,而1<>1是为false,所以后面的也不需要判断了,直接返回else部分。即使是1<>null,根据集合论的特性,NULL和实际数据的对比总是返回unknown,所以也是为false。如果你非要用NOT IN ,请确保子查询永远不会有NULL返回。或者需要额外处理去除NULL,比如:

SELECT  Barcode
FROM dbo.ShipmentItems
WHERE ShipmentBarcode = '123654'
AND Barcode NOT IN ( SELECT Barcode
FROM dbo.ShipmentItems
WHERE ShipmentBarcode = '123456'
AND Barcode IS NOT NULL ) ;

还有一种方法就是改写语句,用NOT EXISTS来等价替换:

SELECT  i.Barcode
FROM dbo.ShipmentItems AS i
WHERE i.ShipmentBarcode = '123654'
AND NOT EXISTS ( SELECT *
FROM dbo.ShipmentItems AS i1
WHERE i1.ShipmentBarcode = '123456'
AND i1.Barcode = i.Barcode );
/*
Barcode
------------------------------
1010204
*/

另外,基于SARG要求,一般不建议用NOT IN/NOT EXISTS这种反向扫描,避免影响性能。还有一个选择使用IN/EXISTS的要点,就是多列匹配的问题,在T-SQL中,多列同时匹配要用EXISTS,而单列匹配可以用EXISTS/IN。可能可以用其他写法来实现IN的多列匹配,但是一般我个人会选择使用EXISTS来匹配多列。

原文出自:CSDN博客:黄钊吉的博客

开发随笔——NOT IN vs NOT EXISTS的更多相关文章

  1. Kinect开发随笔①——红外扫描仪(Kinect 数据源)

    来源于 MVA 的 快速入门:Kinect for Windows v2 开发 的学习随笔 具体内容为上图所示章节内容 章节内全部代码:GitHub地址点我(链接失效,待补档) <Page &l ...

  2. UWP开发随笔——使用SQLite数据库

    摘要 大多数的app都需要数据存储,在数据存储这方面,强大的windows把app数据分为两种:settings和files,并提供了十分简洁的api,让开发者能够轻松使用.但是在有些场景下,app的 ...

  3. 初学安卓开发随笔之 Intent 用法

    首先,对于安卓开发,目前世界上流行的是使用的是Android studio 2.0 .(hh 学着来呗 书上说用这个,,) 今后就定一个计划 每天更新一个Android 随笔,增强一下自控力吧!!! ...

  4. FPGA开发随笔汇总

    点击标题即可进入相关随笔. DE-SOC开发板VrilogHDL开发相关部分: (本过程需要Verilog HDL 的基本语言基础) 1.FPGA的发展史及FPGA 的基础架构 2.首先看一下友晶DE ...

  5. cefSharp 开发随笔

    最近用cefSharp开发一点简单的东西.记录一点随笔,不定时更新. 1.用nuget安装完之后,架构要选择x86或者x64,否则编译会报错(截止到Chrome 55版本) 2.向Chrome注册C# ...

  6. ActiveReport系列报表开发随笔收集

    转自:博客园 http://www.cnblogs.com/dahuzizyd/archive/2007/04/11/ActiveReport_All.html 使用ActiveReport for ...

  7. 如何提高码农产量,基于ASP.NET MVC的敏捷开发框架之移动端开发随笔二

    前言 在前一篇文章中我已经做过开篇,接下来的随笔会详细讲一下我们的开发框架是如何实现的,专业的事由专业的人来讲,以后就由我们的高级码农小李英文名查尔斯和他的师父厂长(因为姓陈,酷爱摄影,我们的文艺片都 ...

  8. com.panie 项目开发随笔(NoF)_环境搭建(2016.12.29)

    (一) 最近做的框架一直在 spring + springmvc + mybatis 的基础上,使用框架的好处自然是 简化了自己的开发工作,定义好大的结构体系后就在里面套用方法了! 可是框架的毛病同样 ...

  9. com.panie 项目开发随笔_前后端框架考虑(2016.12.8)

    (一) 近日和一同学联系,说了我想要做一个网站的打算.她很感兴趣.于是我们协商了下,便觉得一起合作.她写前端,我写后台.因为我对于前端样式设计并不怎么熟悉. (二) 我们决定先做一个 个人博客. 网上 ...

随机推荐

  1. centos安装Chromium

    1. Change root user Shell su - ## OR ## sudo -i 0 1 2 3 4   su - ## OR ## sudo -i   2a. Install NCSU ...

  2. 关于split与StringTokenizer的理解

    关于split与StringTokenizer的理解 一.split    依据匹配给定的正則表達式来拆分此字符串.此方法返回的数组包括此字符串的子字符串,每一个子字符串都由还有一个匹配给定表达式的子 ...

  3. hadoop日志分析

    一.项目要求 本文讨论的日志处理方法中的日志,仅指Web日志.事实上并没有精确的定义,可能包含但不限于各种前端Webserver--apache.lighttpd.nginx.tomcat等产生的用户 ...

  4. VMware workstation 安装错误提示1021解决方法

    Failed to create the requested registry key Key: Installer Error: 1021 解决方法:删除注册表--HKEY_LOCAL_MACHIN ...

  5. 修ecshop品牌筛选以LOGO图片形式显示

    如何实现商品列表页属性筛选区品牌筛选以LOGO形式展示,最模板总结ecshop/'>ecshop教程入下: 1.修改 category.php 文件,将(大概215行) $sql = " ...

  6. Unity多玩家网络游戏开发教程1章Unity带有网络功能

    Unity网络多玩家游戏开发教程第1章Unity自带网络功能 Unity拥有大量的第三方插件.专门提供了对网络功能的支持. 可是.大部分开发人员第一次接触到的还是Unity自带的网络功能.也就是大家常 ...

  7. 黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (高级)

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (高级) 本章介绍的是企业库加密应用程序模块 ...

  8. JSP与ASP.PHP的比較

    眼下,最经常使用的三种动态网页语言为:ASP(Active Server Pages).PHP(HyperText Preprocessor)及JSP(Java Server Pages). JSP与 ...

  9. Android Ant 和 Gradle 打包流程和效率对照

    一.Ant 打包:(下载ant.配置环境变量就不说了) 1.进入命令行模式,并切换到项目文件夹.运行例如以下命令为ADT创建的项目加入ant build支持: android update proje ...

  10. 国庆去学校的国际象棋(Latex)

    国庆节去学棋与朋友. 看国外的网站更有趣的事情. 很快打下来. 首先效应: 嘿嘿  代码来了哟: \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc ...