오라클 데이터베이스를 다루다 보면 특정 집합에 포함되지 않는 데이터를 찾기 위해 NOT IN과 NOT EXISTS 사이에서 고민하게 됩니다.
겉보기엔 비슷해 보이지만, NULL 처리 방식과 성능 면에서 큰 차이가 있습니다.
실무에서 실수하기 쉬운 포인트와 예제를 중심으로 깔끔하게 정리해 드립니다.
1. 한 줄 요약
- NOT IN: 서브쿼리 결과에 NULL이 하나라도 있으면 결과가 나오지 않습니다.
- NOT EXISTS: 서브쿼리 내의 NULL 여부와 상관없이 조건에 맞는 행의 존재 여부만 따집니다.
2. 예제로 보는 결과의 차이
두 방식의 차이를 확인하기 위해 아래와 같은 샘플 테이블이 있다고 가정해 보겠습니다.
[EMP_DEPT (직원 테이블)]
| EMP_ID | DEPT_NAME |
| 1 | 개발 |
| 2 | 디자인 |
| 3 | 기획 |
[RESTRICTED_DEPT (제한된 부서 테이블)]
| DEPT_NAME |
| 개발 |
| NULL |
Case A: NOT IN을 사용하는 경우
SQL
SELECT * FROM EMP_DEPT
WHERE DEPT_NAME NOT IN (SELECT DEPT_NAME FROM RESTRICTED_DEPT);
- 결과: 데이터가 조회되지 않음 (0건)
- 이유: NOT IN은 내부적으로 DEPT_NAME != '개발' AND DEPT_NAME != NULL로 풀립니다. 오라클에서 NULL과의 비교는 항상 Unknown이기 때문에 전체 조건이 만족되지 않습니다.
Case B: NOT EXISTS를 사용하는 경우
SQL
SELECT * FROM EMP_DEPT E
WHERE NOT EXISTS (
SELECT 1
FROM RESTRICTED_DEPT R
WHERE R.DEPT_NAME = E.DEPT_NAME
);
- 결과: 디자인, 기획 (2건)
- 이유: NOT EXISTS는 메인 쿼리의 DEPT_NAME과 서브쿼리의 DEPT_NAME이 일치하는 '행'이 존재하는지만 체크합니다. NULL은 비교 조건(=)에서 일치하지 않으므로 제외되고, 나머지 일치하지 않는 부서들이 정상적으로 출력됩니다.
3. 성능(Performance) 측면
인덱스 활용
- NOT EXISTS: 일반적으로 Anti-Join 방식으로 동작하며, 서브쿼리 테이블의 인덱스를 효율적으로 활용합니다. 메인 테이블의 행마다 서브쿼리를 확인하다가 조건에 맞는 것을 하나라도 찾으면 즉시 멈추기 때문에 대량 데이터 처리 시 유리합니다.
- NOT IN: 과거에는 인덱스 활용이 비효율적이었으나, 최신 오라클 버전(11g 이상)에서는 Null-Aware Anti-Join 기술이 도입되어 성능 차이가 많이 줄었습니다. 하지만 여전히 NULL 체크를 위한 내부 연산이 추가될 수 있습니다.
4. 언제 무엇을 써야 할까?
| 상황 | 추천 연산자 | 이유 |
| 서브쿼리 컬럼에 NULL이 있을 때 | NOT EXISTS | NOT IN은 결과를 0건으로 반환할 위험이 있음 |
| 서브쿼리 컬럼이 NOT NULL일 때 | 둘 다 무관 | 성능 차이가 미미하며 가독성에 따라 선택 |
| 가장 안전한 선택을 원할 때 | NOT EXISTS | 데이터 변화에 유연하게 대응 가능 |
5. 마무리 요약
오라클에서 서브쿼리를 이용해 제외 조건을 걸 때는 NOT EXISTS를 기본으로 사용하는 습관을 들이는 것이 좋습니다. 서브쿼리 컬럼에 NULL이 하나라도 섞여 들어오는 순간 NOT IN은 의도치 않은 결과를 내뱉어 심각한 로직 오류를 발생시킬 수 있기 때문입니다.
Copyright 2026. [Ryon] all rights reserved.