
SQL 쿼리에서 EXITS 와 NOT EXISTS 구문과 이를 JOIN 으로 대체하는 방법에 대해 정리하겠다.
EXISTS vs. JOIN
1. EXISTS 구문
(1) 특징
- 서브쿼리 결과가 존재하는지만 판단
- 첫 번째 행이 발견되면 바로 TRUE 반환 (효율적)
- 메인 쿼리와 서브쿼리 간의 상관 관계가 있음 (Correlation subquery)
(2) 예시
SELECT e.emp_id, e.name
FROM employees e
WHERE EXISTS (
SELECT 1
FROM departments d
WHERE d.dept_id = e.dept_id
AND d.location = 'SEOUL'
);
(3) 장점
- 조건 충족 여부만 판단할 때 명확하고 간결
- 서브쿼리에 인덱스가 잘 설정되면 빠르게 TRUE 확인 가능
- 조건식 기반 필터링에 적합
(4) 단점
- 옵티마이저 최적화 범위가 좁음 (단순 존재 확인만)
- 대량 데이터에선 성능 저하 가능
- 상관 서브쿼리는 반복 실행될 수 있어 비용 증가 가능성
2. INNER JOIN 구문
(1) EXISTS 대체 방식
SELECT e.emp_id, e.name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id
WHERE d.location = 'SEOUL';
(2) 장점
- 옵티마이저가 다양한 조인 전략(HASH, NESTED, MERGE 등) 선택 가능
- 인덱스를 활용한 효율적인 조인 가능
- 명확하게 어떤 데이터가 결합되는지 확인 가능 (디버깅/튜닝 편리)
(3) 단점
- 불필요한 컬럼까지 결합되면 메모리 사용량 증가 가능
- 데이터 중복 결과 조심해야 함
NOT EXISTS vs LEFT JOIN
1. NOT EXISTS 구문
(1) 특징
- 서브쿼리에 해당 조건을 만족하는 행이 없을 때만 TRUE 반환
- Anti-join과 유사한 로직
(2) 예시
SELECT e.emp_id, e.name
FROM employees e
WHERE NOT EXISTS (
SELECT 1
FROM resignations r
WHERE r.emp_id = e.emp_id
);
(3) 장점
- 직관적이고 단순한 구조
- 인덱스를 잘 쓰면 성능 우수
- Null-safe (null이 포함된 조건도 정확히 처리)
(4) 단점
- 서브쿼리 반복 실행 가능성
- 옵티마이저 최적화 여지가 적음
2. LEFT JOIN + IS NULL 대체
(1) NOT EXISTS 대체 방식
SELECT e.emp_id, e.name
FROM employees e
LEFT JOIN resignations r ON r.emp_id = e.emp_id
WHERE r.emp_id IS NULL;
(2) 장점
- 옵티마이저가 Anti-Join 전략(NOT EXISTS)으로 자동 변환하기도 함
- 조인 방식 선택 가능 (HASH, MERGE 등)
- 실무에서는 NOT EXISTS 보다 더 유연한 조정 가능
(3) 단점
- LEFT JOIN 결과에 따라 불필요한 null 포함 가능성
- IS NULL 조건이 누락되면 의도와 다른 결과 발생
# 요약 비교
구분 | EXITS | INNER JOIN | NOT EXISTS | LEFT JOIN + IS NULL |
---|---|---|---|---|
용도 | 존재 확인 | 실데이터 조인 | 존재하지 않음 확인 | 존재하지 않음 확인 |
성능 | 적은 데이터에 효율적 | 대량 데이터에 효율적 | 단순 조건에 적합 | 옵티마이저 활용 유리 |
인덱스 활용 | 제한적 | 적극 활용 가능 | 제한적 | 인덱스 활용 가능 |
가독성 | 간결 | 관계가 명확 | 간결 | 조건 누락시 주의 필요 |
단점 | 옵티마이저 최적화 제한 | 중복, 과조인 주의 | 느릴 수 있음 | NULL 처리 주의 필요 |
추천 시점 | 존재 여부만 중요할 때 | 성능이 중요할 때 | 정확한 배제 조건 필요할 때 | 동일 결과를 JOIN으로 표현하고자 할 때 |
# 실무 팁
- 실제 데이터와 실행 계획(EXPLAIN PLAN)을 보고 판단하세요.
- JOIN으로 바꿨다고 항상 빨라지지는 않습니다.
- 결과가 달라질 수 있으니, 테스트는 필수입니다.
- 옵티마이저 힌트와 함께 사용하는 것도 고려해 보세요. (예: /+ USE_NL /, /+ HASH_JOIN /)
[SQL] EXISTS, NOT EXISTS 의 JOIN 교체