差异b / w'没有'EXISTS'和'MINUS'

时间:2013-12-24 09:42:21

标签: oracle having-clause

HAVING的行为类似于where子句,EXISTS检查给定行的行是否存在。因此,当我们使用HAVING NOT EXISTS时,它应该具有与MINUS相同的功能,从而消除第一个表中的公共行。

但是在我将HAVING NOT EXISTS替换为MINUS的示例中,结果集不相同。查询是

(
   select empno,ename,job,mgr,hiredate,sal,comm,deptno
 ,
        count(*) as cnt
     from  v
    group by empno,ename,job,mgr,hiredate,sal,comm,deptno
    minus
  ( select empno,ename,job,mgr,hiredate,sal,comm,deptno
 ,
          count(*) as cnt
     from emp
    group by empno,ename,job,mgr,hiredate,sal,comm,deptno)
   )
     union all
   (
   select empno,ename,job,mgr,hiredate,sal,comm,deptno
 ,
            count(*) as cnt
     from emp
   group by empno,ename,job,mgr,hiredate,sal,comm,deptno
   minus
  ( select empno,ename,job,mgr,hiredate,sal,comm,deptno
 ,
            count(*) as cnt
     from v
    group by empno,ename,job,mgr,hiredate,sal,comm,deptno)
  )

V是一个视图,形成如

create or replace view v
as
select * from emp where deptno != 10
union all
select * from emp where ename = 'WARD';

查询是确定表是否具有相同数据的示例

这是我的emp表:

  EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
  ---------- ---------- --------- ---------- --------- ---------- ---------- ----------
  7369 SMITH      CLERK           7902 17-DEC-80        800                    20
  7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30
  7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30
  7566 JONES      MANAGER         7839 02-APR-81       2975                    20
  7654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400         30
  7698 BLAKE      MANAGER         7839 01-MAY-81       2850                    30
  7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10
  7788 SCOTT      ANALYST         7566 09-DEC-82       3000                    20
  7839 KING       PRESIDENT            17-NOV-81       5000                    10
  7844 TURNER     SALESMAN        7698 08-SEP-81       1500          0         30
  7876 ADAMS      CLERK           7788 12-JAN-83       1100                    20
  7900 JAMES      CLERK           7698 03-DEC-81        950                    30
  7902 FORD       ANALYST         7566 03-DEC-81       3000                    20
  7934 MILLER     CLERK           7782 23-JAN-82       1300                    10

这是视图V

EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH      CLERK           7902 17-DEC-80        800                    20
7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30
7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30
7566 JONES      MANAGER         7839 02-APR-81       2975                    20
7654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400         30
7698 BLAKE      MANAGER         7839 01-MAY-81       2850                    30
7788 SCOTT      ANALYST         7566 09-DEC-82       3000                    20
7844 TURNER     SALESMAN        7698 08-SEP-81       1500          0         30
7876 ADAMS      CLERK           7788 12-JAN-83       1100                    20
7900 JAMES      CLERK           7698 03-DEC-81        950                    30
7902 FORD       ANALYST         7566 03-DEC-81       3000                    20
7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30

使用minus时,查询结果为

EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO        CNT
------ ---------- --------- ---------- --------- ---------- ---------- ---------- ----------
7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30          2
7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30          1
7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10          1
7839 KING       PRESIDENT            17-NOV-81       5000                    10          1
7934 MILLER     CLERK           7782 23-JAN-82       1300                    10          1

使用HAVING NOT EXISTS时,no rows来自查询

我对这些条款的理解是否有任何错误。请用简短的例子解释为什么结果集不同。谢谢

1 个答案:

答案 0 :(得分:1)

这总是不会产生任何行:

select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt
from  v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
having not exists (
  select empno,ename,job,mgr,hiredate,sal,comm,deptno, count(*) as cnt
  from emp
  group by empno,ename,job,mgr,hiredate,sal,comm,deptno
);

selecthaving子句中查询返回的数据之间没有相关性。除非emp为空,否则该子句中总会存在某些内容,因此having not exists必须为false,因此主查询中的所有行都将被丢弃。

这与你的数据无关;简化版本也是如此:

select * from dual
where not exists (select * from dual);

dual中的行在逻辑上是不可能的,同时dual中没有行。 (好吧,如果dual中没有行,那你就麻烦大了,所以也许这不是一个很好的例子。)

在您的情况下vemp相关;如果emp为空,则v也必须为空。

此部分中的主select找到v中的11行。 select子句中的havingemp中找到14行,但如果它找到1行或100行则无关紧要,因为两组行之间没有关系。 having子句仅用作过滤器; having not exists (<query that finds anything>)过滤掉所有内容。如果您将其分解,exists (select ...)为真,因为emp中有行; not exists (select ...)只是对此的否定,所以它是假的;所以你有效地说'给我行true = false'。

union的第二部分找到emp中的14行,但同样的'true = false'过滤器适用,因此所有行仍然被丢弃,即使内部select只找到v的11行。您未在vemp中的数据之间建立任何关联。

但是任何总是产生行的子查询都会产生相同的效果;因为没有相关性,你可以查询一个与emp完全无关的不同表,并且只要子查询返回一行或多行,你的having子句就会过滤掉你期望的所有内容查看。这表现相同,例如:

select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt
from  v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
having not exists (
  select * from dual
);

对于联合的第二部分,您可以通过添加相关性来获取一些数据:

select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt
from  v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
having not exists (
  select empno,ename,job,mgr,hiredate,sal,comm,deptno, count(*) as cnt
  from emp
  where emp.empno = v.empno
  and emp.ename = v.ename
  and emp.job = v.job
  and emp.mgr = v.mgr
  and emp.hiredate = v.hiredate
  and emp.sal = v.sal
  and emp.deptno = v.deptno
  group by empno,ename,job,mgr,hiredate,sal,comm,deptno
);

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO        CNT
---------- ---------- --------- ---------- --------- ---------- ---------- ---------- ----------
      7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10          1 
      7934 MILLER     CLERK           7782 23-JAN-82       1300                    10          1 
      7839 KING       PRESIDENT            17-NOV-81       5000                    10          1 

如果它是主键或唯一,只使用empno可能有效,除非你真的在寻找其他字段之间的差异。但是将vemp之间的计数关联起来很棘手,可能不值得付出努力。看着伯爵看起来有点奇怪;它们仅因WARD而有所不同,因为您已经构建了视图。在这里使用minus更简单,也可能更快。