Skip to Main Content
  • Questions
  • not exists and not in alternative queries.

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Amir.

Asked: January 14, 2014 - 1:47 pm UTC

Last updated: January 15, 2014 - 7:50 pm UTC

Version: 11g

Viewed 10K+ times! This question is

You Asked

Hi Tom,

I have two questions first why the first query is giving with different result I am expecting it to give me same result as

select * from emp where deptno not in (select deptno from dept)
but this query

select distinct e.*
From emp e left join dept d
on ( e.deptno = d.deptno
and d.deptno is null )

gives me different result. whats the logic i am missing here.


select * from emp e where not exists ( select null from dept d where d.deptno=e.deptno)

select e.*
From emp e left join dept d
ON ( e.deptno = d.deptno )
where d.deptno is null

This part works fine.



and Tom said...

look at your outer join condition.


on ( e.deptno = d.deptno

and d.deptno is null )



you have said - I want every record from EMP joined to a DEPT record such that the DEPT record has the same deptno AND the DEPT records deptno is NULL.

that can never happen - that is never true. So, as far as we are concerned - every record in the EMP table will be output from that - because you asked us to outer join from EMP to DEPT on that condition (that will NEVER be true).


Now, look at the second one:

ON ( e.deptno = d.deptno )

where d.deptno is null



you said to join EMP to DEPT by deptno (every record in EMP has a mate in DEPT, so every record comes out of the join) AND THEN, after doing the outer join (which wasn't necessary in this case, a natural join would have sufficed since every EMP record has a mate in DEPT) - only keep rows such that the DEPT.DEPTNO was null - which none are of course.




Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

confusing

Amir Riaz, January 14, 2014 - 11:20 pm UTC

Hi Tom,

now i am confused than before. why the deptno is null condition is not true.

select distinct e.*
From emp e left join dept d
on ( e.deptno = d.deptno
and d.deptno is null )

you have said - I want every record from EMP joined to a DEPT record such that the DEPT record has the same deptno AND the DEPT records deptno is NULL.

that can never happen - that is never true. So, as far as we are concerned - every record in the EMP table will be output from that - because you asked us to outer join from EMP to DEPT on that condition (that will NEVER be true).

what will be the alternative sql for this query

select * from emp where deptno not in (select deptno from dept)

provided, it shows no record of emp when deptno has nulls. dont give null records when deptno in emp is null




Tom Kyte
January 15, 2014 - 7:50 pm UTC

when you JOIN on:

( e.deptno = d.deptno and d.deptno is null )


it is like you are saying:

for x in ( select * from emp )
loop

   open c for 
   select * 
     from dept 
    where X.DEPTNO = dept.deptno 
      AND dept.deptno is null;
   -- the above cursor will NEVER fetch a row, never
   -- the ON join clause you gave prevents it from
   -- seeing any data since dept.deptno would have to simultaneously
   -- be equal to X.DEPTNO and it  would have to be NULL.  that will
   -- never ever happen

   -- that means that EVERY record in EMP, by definition, will be output
   -- with null's for every DEPT attribute.

   fetch C into some_record;
   if c%notfound 
   then
       output EMP without any DEPT information (outer join)
   else
       loop
          output EMP with some_record
          fetch c into some_record;
          exit when c%notfound;
       end loop
   end if;
end loop;




when you join on

select e.*
From emp e left join dept d
ON ( e.deptno = d.deptno )
where d.deptno is null


that is like saying:

for x in (select * from emp)
loop
   open c for
   select * 
     from dept
    where X.DEPTNO = dept.deptno;

   fetch C into some_record;
   if c%notfound 
   then
       output EMP without any DEPT information (outer join)
       -- this never happens since EMP.DEPTNO always has a value 
       -- and EMP.DEPTNO is a foreign key to DEPT.DEPTNO
   else
       loop
          if ( some_record.deptno is null )
          then
               output EMP with some_record
               -- this never happens since every emp.deptno is in dept.deptno
          end if;
          fetch c into some_record;
          exit when c%notfound;
       end loop
   end if;
end loop;





the ON clause is used to JOIN (it is used to perform the natural or outer join) AND THEN the predicate is applied to those rows.


when you outer join using an ON condition that is never true, the table that is driving the join will just be output "as is".

consider:

ops$tkyte%ORA11GR2> select emp.empno, dual.*
  2    from emp left outer join dual on (1=0)
  3  /

     EMPNO D
---------- -
      7788
      7369
      7499
      7521
      7566
      7654
      7698
      7782
      7839
      7844
      7876
      7900
      7902
      7934

14 rows selected.



that query is semantically equivalent to "select * from emp". since the ON condition (the join condition) can never be true - EMP will just be output.



The alternative for:

select * from emp where deptno not in (select deptno from dept)


would/could be (using the fact that DEPTNO is a primary key of DEPT and hence, DEPT.DEPTNO *cannot be null* - if dept.deptno was not a primary key - was allowed to have nulls - this would NOT be an equivalent query)

select emp.*
  from emp left outer join dept on (emp.deptno = dept.deptno)
 where dept.deptno is NULL;



if dept.deptno is allowed to be null, it would be:

select emp.*
  from emp left outer join dept on (emp.deptno = dept.deptno)
 where dept.deptno is NULL
   and not exists (select null from dept where deptno is null);