了解一下NULLs怎样影响IN和EXISTS

如果你的数据库设计在任何一栏中都允许NULL值的话,你需要了解一下,在你的查询语句中,不同的子句是怎样对待这一问题的。
  
  从表面上看,可能显示出这样的情形,即SQL子句IN与EXISTS可以互换。然而,在处理NULL值时,它们的表现截然不同,而且得到的结果也很可能不同。问题源于这样一个事实,即在一个Oracle数据库中,一个NULL值意味着未知,因此,对一个NULL值的任何比较或操作也都是无效的,而任何返回NULL的测试也都被忽视了。例如,以下这些查询语句都不会返回任何行:
  
  select 'true' from dual where 1 = null;
  
  select 'true' from dual where 1 != null;
  
  值1既不能说是等于NULL,也不能说是不等于NULL。只有是NULL的时候才会返回一个真正的NULL值并返回一行。
  
  select 'true' from dual where 1 is null;
  
  select 'true' from dual where null is null;
  
  当你使用IN时,相当于你告诉SQL接受一个值,并将它与某个清单中使用=的每一个值或一组值进行比较。只要存在了任何NULL值,就不会返回任何行,纵使两个值都是NULL也不行。
  
  select 'true' from dual where null in (null);
  
  select 'true' from dual where (null,null) in ((null,null));
  
  select 'true' from dual where (1,null) in ((1,null));
  
  一个IN从功能上等同于=ANY子句:
  
  select 'true' from dual where null = ANY (null);
  
  select 'true' from dual where (null,null) = ANY ((null,null));
  
  select 'true' from dual where (1,null) = ANY ((1,null));
  
  当你使用一种与EXISTS等同的格式时,SQL会计算行数,却忽视子查询中的值,就算你返回NULL也一样。
  
  select 'true' from dual where exists (select null from dual);
  
  select 'true' from dual where exists (select 0 from dual where null is null);
  
  从逻辑上看,IN与EXISTS是一样的。IN子句在外部查询中比较子查询返回的值,并过滤掉行;EXISTS子句在子查询内部比较那些值并过滤掉行。在出现NULL值的情况下,作为结果而出现的那些行是相同的。
  
  selectename from emp where empno in (select mgr from emp);
  
  selectename from emp e where exists (select 0 from emp where mgr = e.empno);
  
  不过,当逻辑被转变成使用NOT IN和NOT EXISTS时,问题就出现了,这两个语句会返回不同的行(第一个查询会返回0行;第二个返回意想的数据-它们是不同的查询):
  
  selectename from emp where empno not in (select mgr from emp);
  
  selectename from emp e where not exists (select 0 from emp where mgr =e.empno);
  
  NOT IN子句实际上与用=比较每一个值相同,如果任何一个测试为FALSE 或NULL的话,它就会失败。例如:
  
  select 'true' from dual where 1 not in (null,2);
  
  select 'true' from dual where 1 != null and 1 != 2;
  
  select 'true' from dual where (1,2) not in ((2,3),(2,null));
  
  select 'true' from dual where (1,null) not in ((1,2),(2,3));
  
  这些查询不会返回任何行。而第二个更值得怀疑,1!=NULL是NULL,因此对整个WHERE条件来说都是错误的。它们会这样运行:
  
  select 'true' from dual where 1 not in (2,3);
  
  select 'true' from dual where 1 != 2 and 1 != 3;
  
  只要你在结果中阻止系统返回NULL,在这之前你还是可以使用NOT IN查询(同样,这些都能运行,不过我假定empno不是NULL,在我们这个案例中,这是一个很好的假设):
  
  selectename from emp where empno not in (select mgr from emp where mgr is not null);
  
  selectename from emp where empno not in (select nvl(mgr,0) from emp);
  
  由于了解了IN,EXISTS,NOT IN,以及NOT EXISTS之间的差别,当一个子查询的数据中出现NULL时,你就可以避免一个非常普遍的问题了。

时间: 2024-11-18 04:33:09

了解一下NULLs怎样影响IN和EXISTS的相关文章

nulls first & nulls last 对索引影响

-- 当我们需要排序字段时,比如order by name,如果name字段定义时没有not null时,就有可能涉及到null值的排序 -- 如果不注意,可能会造成隐藏的bug,pg默认null是无穷大,在升序时排在最后面,当然在排序时也可以指定 nulls first 或 nulls last -- 具体使用方法在此不在复述,本文主要是讲的是在创建索引时指定 nulls first 或 nulls last 对查询的影响 -- 基础创建索引语法如下 CREATE INDEX [ ASC |

oracle中not exists对外层查询的影响

又一个类似『12c比10g索引回表消耗增多的问题』的案例,同事在12c中跑的buffer get很高,但是在10g中跑的buffer很低.怀疑是不是12c的优化器有问题. 这个10g的环境和12c的环境,数据量大致一样,只是有很少部分的不同,但是就是这个很少部分不同,造成了not exists中的子查询返回不同的值,进而对外层查询产生不同的影响. 我们来用如下的代码模拟一下. 初始化数据: --10g drop table t1; drop table t2;   create table t1

SQL视图对性能的影响

近日在生产库ADDM中捕捉到了一些列SQL语句,绝大部分通过索引优化都能够搞定,但是如下一句SQL语句却非常难缠.通过索引优化效果一直不理想,最后进一步分析得出该语句上层视图用了×查询出了ACH大数据表的所有字段(数百万记录,50个字段的表),而我们在SQL中只用到了区区几个字段,于是决定抛弃该视图,把视图中相关逻辑直接在上层SQL中展现.通过该动作,大大降低了对关键表的查询开销,得以高效优化. 问题SQL语句: Select Count(Distinct e.Entry_Id) From v_

sql查询语句的优化,exists与in的更换

自己开发的小站,页面访问查询的速度一直不让人满意,刚好今天有时间,就决定对它优化一下. 因为在本地开时发,查询的速度是相当快的,一开始就以为是mysql版本的问题,本地是MariaDB 5.5,服务器上是mysql 5.1, 将服务器上的数据弄到本地导了一份,居然发现一样变慢了,平均查询一个文章要1-2秒,列表超过2秒以上,才几千的数据这么慢肯定是哪里出了问题. 一点一点的定位打印日志,最后发现,居然是一条使用了exists的sql语句,查询就用了2秒,也就是说时间都花在这个上了,其它的基本都可

从一个简单的约束看规范性的SQL脚本对数据库运维的影响

原文:从一个简单的约束看规范性的SQL脚本对数据库运维的影响   之前提到了约束的一些特点,看起来也没什么大不了的问题,http://www.cnblogs.com/wy123/p/7350265.html以下以实际生产运维中遇到的一个问题来说明规范的重要性. 如下是一个简单的建表脚本,表面上看起来并没有什么问题.其中创建了3个约束,一个主键约束,一个唯一约束,一个默认值约束,该脚本执行起来没有任何问题. USE Test GO if exists(select 1 from sys.table

SQL Server用NOT EXISTS或(外连接+判断为空)方案实例

用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格. 但是用IN的SQL性能总是比较低的,从SQL执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别: SQL试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询.由此可见用IN的SQL至少多了一个转换的过程.一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了. 推荐在业务密集的SQL当中尽量不采用IN操作符 NO

利用硬链接和truncate降低drop table对线上环境的影响

作者简介 肖鹏 微博研发中心数据库技术负责人,主要负责微博数据库(MySQL/Reids/HBase/Memcached)相关的业务保障,性能优化,架构设计以及周边的自动化系统建设.10年互联网数据库架构和管理经验,专注于数据库的高性能和高可用技术保障方向. 众所周知drop table会严重的消耗服务器IO性能,如果被drop的table容量较大,甚至会影响到线上的正常. 首先,我们看一下为什么drop容量大的table会影响线上服务 直接执行drop table,mysql会将表定义和表数据

IN&EXISTS与NOT IN&NOT EXISTS 的优化原则小结_MsSql

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 对于in 和 exists的性能区别: 如果子查询得出

探究MySQL中索引和提交频率对InnoDB表写入速度的影响_Mysql

本次,我们来看看索引.提交频率对InnoDB表写入速度的影响,了解有哪些需要注意的. 先直接说几个结论吧: 1.关于索引对写入速度的影响: a.如果有自增列做主键,相对完全没索引的情况,写入速度约提升 3.11%: b.如果有自增列做主键,并且二级索引,相对完全没索引的情况,写入速度约降低 27.37%: 因此,InnoDB表最好总是有一个自增列做主键. 2.关于提交频率对写入速度的影响(以表中只有自增列做主键的场景,一次写入数据30万行数据为例): a.等待全部数据写入完成后,最后再执行com