IN, EXISTS
비슷한 듯, 동작방식이 다른 두 연산에 대해 알아보자.
select DISTINCT deptno from dept
select DISTINCT deptno from dept
select DISTINCT deptno from emp
IN 동작방식
근본적으로 IN 은 OR 동작과 같다.
select DISTINCT deptno
from dept
where deptno in (select DISTINCT deptno from emp)
위 쿼리에 서브 쿼리 결과는 아래와 같다.
select DISTINCT deptno from emp
즉, 위 쿼리는 아래와 같은 결과를 도출한다.
SELECT *
FROM DEPT
WHERE DEPTNO IN
(30
,NULL
,20
,10)
실행결과는 둘 다 같다.
EXISTS
select *
from dept d
where EXISTS (select deptno from emp e WHERE e.DEPTNO = d.DEPTNO)
위 쿼리 또한 같은 결과를 도출한다.
IN과 차이는 EXISTS는 서브쿼리가 행을 반환하면 TRUE를 도출한다. 즉, 직접적으로 값 비교를 하지 않는다.
NOT IN, NOT EXISTS
단순히 위 쿼리 2개에 NOT만 붙이면 어떻게 될까?
select distinct deptno
from dept
where deptno not in (select distinct deptno from emp)
아마 위와 같은 결과를 기대했을 것이다.
하지만 아래와 같은 결과가 나온다.
SELECT DISTINCT DEPTNO
FROM DEPT D
WHERE NOT EXISTS (SELECT DISTINCT DEPTNO FROM EMP E WHERE E.DEPTNO = D.DEPTNO)
NOT EXISTS는 정상적으로 기대한 값이 나온다.
왜 이런 결과가 나오는 것일까?
SELECT DISTINCT DEPTNO
FROM DEPT
WHERE DEPTNO NOT IN
(30
--,NULL
,20
,10)
위 쿼리에서 봤던 풀이에서 NULL만 주석 처리하고 결과를 보면 같은 결과가 나온다.
위 결과가 도출된 이유는 아래와 같다.
SELECT DISTINCT DEPTNO FROM DEPT;
SELECT DISTINCT DEPTNO FROM EMP;
각 테이블의 결과를 데카르트 곱을 하게 된다.
10을 예로 들면
(10=10, 10=20, 10=30, 10=NULL)
IN은 근본적으로 OR연산이니
(10=10 OR 10=20 OR 10=30 OR 10=NULL)
(TRUE OR FALSE OR FALSE OR NULL)
OR연산에서 하나라도 TRUE가 있으면 FALSE가 몇 개던 TRUE이다.
(TURE OR NULL)
여기가 중요하다 OR은 NULL과 연산해도 TRUE를 도출한다.
(TURE)
즉, 각 값마다 데카르트 곱을 할 때 NULL이 껴 있느면 그냥 IN일때는 TRUE를 반환하지만
NOT IN 일 경우 TRUE에 NOT이 되어 전부 FALSE가 되는 것이다.
SELECT DISTINCT DEPTNO
FROM DEPT D
WHERE NOT EXISTS (SELECT DISTINCT DEPTNO FROM EMP E WHERE E.DEPTNO = D.DEPTNO)
EXISTS의 경우에는 각 행 단위로 행 결과만 존재 여부로 TRUE, FALSE를 리턴하기 때문에 정상적으로 값이 도출되는 것이다.
이래나 저래나 당장 이해하기 버겁다면, NOT IN만 조심하면 된다는 것을 기억하면 된다.
아니 NOT IN에 NULL이 끼어있으면 값이 제대로 도출이 안된다는 것만 기억하면 된다.
이를 방지하기 위해선 위 처럼 NOT EXISTS를 사용하거나
기존 쿼리 구조를 변경하기 어려운 경우라면 NULL처리를 적당해 해주면 된다.
SELECT DISTINCT DEPTNO FROM DEPT
WHERE DEPTNO NOT IN (SELECT DISTINCT NVL(DEPTNO, 0) FROM EMP)
굳이 NVL아니더라도 DECODE, COALESCE.... 등 편한대로 처리하면 된다.
'개발 > 오라클 SQL' 카테고리의 다른 글
오라클 비밀번호 만료 (0) | 2022.11.27 |
---|---|
문자열 다루기 핵심 TRANSLATE (1) | 2022.11.25 |
마이바티스 null 체크 (0) | 2022.09.28 |
정규식을 통한 Mybatis Sql Injection 처리 (0) | 2022.06.29 |
바인드 변수 두 개를 받아 그 사이 달력 찍기 (0) | 2022.06.02 |