选取不在另一张表中记录的方法及优化

假设我们想从A表中选择一些记录,记录中的部分字段的取值是B表
所不存在的,这里定义A表为源表,B表为参照表。例如,常见的例子
部门表(Department)作为源表,员工表(Employee)作为参照表,可以
从部门表中列举出那些员工表中所不包含的部门id来,即找出那些没有
员工的部门。
 
  一般类来说,如果数据库支持集合操作符,就可以直接查询,如果
没有,就使用子查询。
(1)
DB2 and
PostgreSQL:
select deptno from dept
except
select deptno from
emp
(2)
Oracle and Teradata:
select deptno from dept
minus
select
deptno from emp
(3)
MySQL and SQL Server:
select deptno
  from
dept
where deptno not in (select deptno from emp)

对于(1)和(2)没有什么多说的,就是需要注意满足集合操作符使用的一般
条件就可以了。而且返回的结果是不重复的(duplicate
removed)。所以对于3
使用者如果对结果重复性有要求的话,就需要自己根据情况添加是否使用
Distinct关键词。

另外一个很显著的区别在于参照表中进行比较的字段是否包含NULL。这个
问题对于集合操作符来说没有任何影响,DBMS本身实现操作符的时候已经解决了。
而使用not
in的子查询方式就需要采取处理措施。
    例如:源表dept中deptno取值有10,20,30,40
         
参照表new_dept中deptno取值有10,50,null
         
SQL A:
     select a.deptno
from dept a where a.deptno
         not in (select deptno from
new_dept)
   
虽然源表中20,30,40这三个deptno都不存在于参照表中,但是上面这个SQL
的查询结果为空,一条记录也没有。
而想要得到需要的结果需要写成:SQL
B
     select a.deptno from dept a where a.deptno
         not in (select
deptno from new_dept where deptno is not null)
查询结果为 20,30,40三条记录

SQL
A之所以没有任何结果的原因在于NULL, NOT,和OR的几个操作符的综合作用
首先IN操作符等价多个OR的并列,所以SQL A实际上等价为
 
   A-1:
          select a.deptno from dept a where a.deptno
         not
in(10,50,null)
     A-2:
          select deptno from dept where
    
    not  (deptno=10 or deptno=50 or
deptno=null)
下面要注意,必须明确几个操作符之间的结果:
  TRUE or NULL   结果是TRUE
  FALSE or
NULL  结果是NULL
  
NOT的运算结果
   bool表达式     NOT 运算结果
     TURE       
   FALSE
     FALSE           TRUE
     NULL          
 NULL
尤其注意NOT对未知的字段运算结果还是NULL,这一点一定要和
IS NULL 和IS NOT
NULL这个关键词组合区分开。

最后任何字段和NULL进行运算=,+,-,...结果都是NULL
这也是SQL里面强调用IS NULL和IS
NOT NULL来区分字段是否为空
而不是用=来区分

明确了上面运算符的运算规则,我们来看
not  (deptno=10 or
deptno=50 or deptno=null)
当源表中deptno为10时,则
not ( TRUE or FALSE or NULL
)
结果是 not (TRUE) -> FALSE  这条记录不会显示
当源表中dept为20时, 则
not ( FALSE or
FALSE or NULL )
结果是 not (NULL) -> NULL 
 
where的搜索条件是空,也不会有任何结果
(你可以尝试select * from dept where
deptno=null这种sql看看结果)

综上,无论源表的记录取何值,最终都得不到查询结果。。。

如果想要得到结果,必须写成SQL
B的格式。

如果觉得始终要为总要当心字段是否为空,而不得不在SQL中注意比较麻烦的情况下
(比如在复杂的查询中,源表和参照表之间需要比较的字段比较多,每个字段都要
考虑NOT
NULL,并且书写出来,确实比较繁琐),可以使用相关子查询
(correlated subquery)来巧妙的避免这个干扰

SQL
C:
        select *
          from dept a
         where not exists
(select null from new_dept b where b.deptno =a.deptno
)
这个查询同样能够得到需要的结果

Step 1:   子查询中执行b.deptno =a.deptno 两个表join操作
Step
2:   如果子查询返回结果,即子查询包含行,则exists(subquery)返回TRUE,
          而not
exists(subquery)返回FALSE,最终查询抛弃当前记录
Step 2: 
 如果子查询没有结果,即子查询不包含任何行,则exists(subquery)返回FALSE,
          而not
exists(subquery)返回TRUE,最终查询返回当前记录

这里需要注意的是在使用exists情况下,其子查询中的select部分并不重要。
因为exists的含义是exists(subquery),只要子查询中包含行,无论是什么样的行记录,
结果就是TURE而SQL
C中的subquery 中只要b.deptno和a.deptno相等,就会有行记录,
而select null from new_dept b where
b.deptno =a.deptno 只不过是说两个deptno相等
返回的行记录是null(null也是一条行记录啊),所以exists(select
null)也是TURE
因此,这种写法就保证了,只要源表中的deptno记录不存在在参考表中,就能够得到结果了。

子查询中的select对结果不会有任何影响,所以写成select
null好像是一种惯例,
我觉得是为了强调相关查询的等价条件而已,但这种符合英语国家的惯例对很多
初次遇到这种写法的人来说,反而被迷惑了一把。。。

只要把握了几个关键的操作符的运算规则,就能够理解这些不同写法所造成的不同结果了。
希望上面的总结能够对大家理解这种查询方式有所帮助。

至于效率方面,exists是否快于in操作,个人持保留意见,这主要看具体的SQL书写条件
和DBMS系统的优化代码。

但是上面的语句SQL
C比SQL B的执行效率要高,这一点是可以解释的。
SQL B使用in 模式,这样实际上是全表扫描,没有任何优化手段,两个表都在遍历全表
而SQL
C使用相关查询,两个表是内联操作,大多数数据库系统都会使用Hash Join来
进行优化查询,所以效率会比较高。

时间: 2024-10-06 09:16:01

选取不在另一张表中记录的方法及优化的相关文章

工作同时删除2张表中记录

mysql: delete a.*,b.* from sys_z_info a,sys_o_info b where a.z_id=b.d_id and a.z_id='123456' 这个呢在只有1张表中有记录的时候不会起作用,不过可以考虑这样做:delete a.*,b.* from sys_z_info a,sys_o_info b where (a.z_id=b.d_id and a.z_id='123456' ) or  a.z_id='123456';这样就可以避免b中没有数据时不做

SQL语句:关于复制表结构和内容到另一张表中的SQL语句

1.复制新表结构及数据到新表create table 新表 select * from 旧表 2.只复制表结构到新表 create table 新表 select * from 旧表 where 1=2 3.复制旧表的数据到新表(假设两个表结构一样) insert into 新表 select * from 旧表 4.复制旧表的数据到新表(假设两个表结构不一样) insert into 新表(字段1,字段2,......) select 字段1, 字段2,... from 旧表 SQL语句:关于

mysql统计一张表中条目个数的方法

统计一张表中条目的个通常的SQL语句是: select count(*) from tableName; #or select count(1) from tableName; #or 统计一个列项,如ID select count(ID) 另外,可通过使用information_schema统计个数 MySQL中有一个名为 information_schema 的数据库,在该库中有一个 TABLES 表,这个表主要字段分别是: TABLE_SCHEMA : 数据库名 TABLE_NAME:表名

如何查出同一张表中字段值重复的记录

如何查出同一张表中字段值重复的记录 (2013-03-25 11:28:51) 转载▼ 比如现在有一人员表(表名:peosons) 若想将姓名.身份证号.住址这三个字段完全相同的记录查询出来select p1.* from persons p1,persons p2where p1.idp2.id and p1.cardid = p2.cardid and p1.pname = p2.pname and p1.address = p2.address 可以实现上述效果. 几个删除重复记录的SQL

Sql从一张表中更改另一张表数据

语法: update table1 set table1.列=table2.列 from table2 where table2.列=table1.列update NFYSP.dbo.PATIENT     setPATIENT_ADDUSERID=OLD.PATIENT_ADDUSERID,PATIENT_UPDATEUSERID=OLD.PATIENT_UPDATEUSERID,PATIENT_UPDATEUSERNAME=OLD.PATIENT_UPDATEUSERNAME,PATIENT

oracle定时器,调用存储过程,定时从n张表中取值新增到本地一张表中

--创建新增本地数据库的存储过程 create or replace procedure pro_electric_record as begin insert into electric_meter_record(id,basestation_id,name,meter_number,createtime) select sys_guid(),substr(s.sname,0,36),s.sname,s.svalue,sysdate from rtdbvs s where s.sname li

如何判断多个字段组成的关键字在另外一张表中是否存在

如何判断多个字段组成的关键字在另外一张表中是否存在 老帅(20141107) 1.首先判断一个关键字在另外一张表中是否存在很容易! SELECT * FROM a WHERE a.ID IN ( SELECT b.ID FROM b ) 2.如果判断的关键字有多个字段构成怎么办呢? 你不能在IN中使用多个字段.如下查询: SELECT * FROM a WHERE (a.ID1, a.ID2) IN ( SELECT b.ID1, b.ID2 FROM b ) 这不会正常工作,违反了SQLSER

取得一张表的数据不在另一张表中的最优秀方法(JOIN与EXISTS的效率研究)

业务需求: 公司有个CRM经纪人管理系统,前天遇到的比较棘手的问题是,要查询一张表中不在另一张表中的所有用户并分页显示,但问题是外表的数据量很大,如果用not in(select ..),not exists(select ...)之类的子查询 的话需要对子查询表进行所有信息的调用过滤才行,一单子查询数据量过大效率问题就来了,于是就私下里查了一点资料,并根据资料中提供的例子建立了对应的测试表做了一些测试,随后将解决问题的方法移交给了技术部的邹鸿同事去解决问题,因为是前天的事情了,本来是事情忙没时

【SqlServer】【问题收集】删除同一张表中完全相同的记录

1   概述 在Sqlserver中,当通过SqlServer设计器删除同一张表中两条完全相同的记录时,会弹出如下提示: 点击“是” 弹出如下提示,不让删除 2   问题解决 这个问题很简单,用DELETE语句删除即可. 如果清除表格所有数据 DELETE EmployeeInfo 如果仅仅删除相同的记录 DELETE EmployeeInfo WHERE EmployyeID='NX001' 3   版权 感谢您的阅读,若有不足之处,欢迎指教,共同学习.共同进步. 博主网址:http://ww