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


        假设我们想从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来
    进行优化查询,所以效率会比较高。

  • 相关阅读:
    ubuntu 防止软件包自动更新
    记录一个开头带有&#x的特征数据的解码
    从一次失败的微信小程序抓包、反编译经历中学习反思
    Python爬虫处理奇葩的请求参数payload
    python爬虫
    python爬虫处理在线预览的pdf文档
    python高级—— 从趟过的坑中聊聊爬虫、反爬、反反爬,附送一套高级爬虫试题
    djangorestframework+vue-cli+axios,为axios添加token作为headers踩坑记
    django 问题综合
    vue(9)—— 组件化开发
  • 原文地址:https://www.cnblogs.com/lbnnbs/p/4781525.html
Copyright © 2020-2023  润新知