
Oracle에서 SQL을 작성하다 보면 한 번쯤은 “EXISTS와 IN 중 어떤 것을 사용하는 것이 더 빠를까?” 라는 고민을 하게 됩니다.
인터넷을 검색해 보면 “EXISTS가 IN보다 빠르다”, “무조건 EXISTS를 사용해야 한다”는 글을 쉽게 찾아볼 수 있습니다.
저 역시 처음에는 그렇게 알고 있었습니다.
하지만 실제 업무에서 SQL 튜닝을 하면서 실행계획을 확인해 보니, 항상 EXISTS가 더 빠른 것은 아니라는 사실을 알게 되었습니다.
이번 글에서는 EXISTS와 IN의 차이점, 각각 어떤 상황에서 사용하는 것이 좋은지, 그리고 실제 성능을 판단할 때 가장 중요하게 확인해야 하는 것이 무엇인지 정리해 보겠습니다.
EXISTS란?
EXISTS는 조건을 만족하는 데이터가 존재하는지만 확인하는 연산자입니다.
조건을 만족하는 데이터가 하나라도 발견되면 TRUE를 반환하며, 더 이상 데이터를 찾지 않아도 됩니다.
예를 들어 다음과 같은 쿼리가 있습니다.
SELECT *
FROM EMP E
WHERE EXISTS (
SELECT 1
FROM DEPT D
WHERE D.DEPT_NO = E.DEPT_NO
);
위 쿼리는 EMP 테이블의 부서번호(DEPT_NO)가 DEPT 테이블에 존재하는 직원만 조회합니다.
여기서 중요한 점은 실제 데이터를 가져오는 것이 아니라 존재 여부만 확인한다는 것입니다.
IN이란?
IN은 특정 값이 목록 안에 포함되어 있는지 비교할 때 사용하는 연산자입니다.
SELECT *
FROM EMP
WHERE DEPT_NO IN (
SELECT DEPT_NO
FROM DEPT
);
위 쿼리는 DEPT 테이블에 존재하는 부서번호(DEPT_NO)를 가진 직원만 조회합니다.
결과는 EXISTS와 동일할 수 있지만, SQL을 읽는 관점에서는 조금 다른 의미를 가집니다.
EXISTS와 IN의 차이점
| EXISTS | IN |
|---|---|
| 존재 여부를 확인 | 값이 목록에 포함되는지 확인 |
| 상관 서브쿼리에서 자주 사용 | 목록 비교에 자주 사용 |
| 조건을 만족하면 종료 가능 | 결과 집합과 비교 |
| NOT EXISTS와 함께 많이 사용 | NOT IN과 함께 많이 사용 |
EXISTS가 항상 더 빠를까?
결론부터 말하면 아닙니다.
예전에는 EXISTS가 IN보다 빠르다는 이야기가 많았습니다.
Oracle의 옵티마이저(CBO)는 SQL을 분석한 뒤 경우에 따라 EXISTS와 IN을 동일하거나 매우 유사한 실행계획으로 변환합니다.
따라서 단순히 EXISTS와 IN만 보고 성능을 판단하기보다는 실제 실행계획을 확인하는 것이 중요합니다.
즉,
- EXISTS를 사용했다고 항상 빠른 것도 아니고,
- IN을 사용했다고 항상 느린 것도 아닙니다.
실제 성능은 다음과 같은 요소에 의해 결정됩니다.
- 데이터의 양
- 인덱스 유무
- 통계 정보
- 옵티마이저
- 실행계획(Execution Plan)
결국 SQL 문법보다 실행계획이 훨씬 중요한 경우가 많습니다.
그렇다면 언제 EXISTS를 사용할까?
저는 존재 여부를 확인하는 목적이라면 EXISTS를 사용하는 편입니다.
예를 들어
SELECT *
FROM PRODUCT P
WHERE EXISTS (
SELECT 1
FROM ORDER_DETAIL OD
WHERE OD.PRODUCT_ID = P.PRODUCT_ID
);
이 쿼리는
한 번이라도 판매된 상품
을 찾는 것이 목적입니다.
이럴 때는 EXISTS가 의미적으로도 자연스럽고 읽기 쉽습니다.
그렇다면 언제 IN을 사용할까?
반대로 특정 목록과 비교하는 경우에는 IN이 더 적합하다고 생각합니다.
예를 들어
SELECT *
FROM ORDERS
WHERE STATUS IN ('READY', 'SHIPPING', 'DELIVERED');
위 쿼리는 주문 상태가 ‘배송준비’, ‘배송중’, ‘배송완료’인 주문만 조회합니다.
비교 대상이 명확한 목록이기 때문에 EXISTS보다 IN을 사용하는 것이 훨씬 직관적입니다.
NOT EXISTS와 NOT IN은 주의할 점이 있다
실무에서는 오히려 NOT EXISTS와 NOT IN의 차이를 더 많이 접하게 됩니다.
예를 들어
MEMBER
ID
----
1
2
NULL
이라면
WHERE ID NOT IN (
SELECT ID FROM MEMBER
)
은 아무 결과도 나오지 않을 수 있습니다.
왜냐하면 NULL과의 비교 결과는 TRUE도 FALSE도 아닌 UNKNOWN이기 때문입니다.
반면
WHERE NOT EXISTS (
SELECT 1
FROM MEMBER M
WHERE M.ID = A.ID
)
는 NULL의 영향을 받지 않기 때문에 실무에서는 NOT EXISTS를 선호하는 경우가 많습니다.
제가 실무에서 사용하는 기준
저는 EXISTS와 IN을 선택할 때 성능보다 SQL의 목적을 먼저 생각합니다.
- 존재 여부를 확인하는 경우 → EXISTS
- 특정 목록과 비교하는 경우 → IN
그리고 성능이 중요한 쿼리라면 EXISTS와 IN 중 무엇을 사용했는지보다 실행계획을 먼저 확인합니다.
같은 결과를 반환하는 쿼리라도 실행계획에 따라 성능이 크게 달라질 수 있기 때문입니다.
마무리
EXISTS와 IN은 서로 비슷한 기능을 수행하지만 목적은 조금 다릅니다.
예전에는 EXISTS가 항상 더 빠르다고 알려져 있었지만, 최근 Oracle에서는 옵티마이저가 SQL을 최적화하기 때문에 단순히 EXISTS와 IN만으로 성능을 판단하기는 어렵습니다.
개인적으로는 “EXISTS가 빠르다”, “IN이 느리다”라는 공식보다는 실행계획을 확인하고 상황에 맞는 SQL을 작성하는 습관이 더 중요하다고 생각합니다.
결국 SQL 성능은 EXISTS와 IN 중 무엇을 선택했는지가 아니라, 옵티마이저가 어떤 실행계획을 선택했는지에 의해 결정되는 경우가 많습니다. 따라서 성능이 중요한 SQL이라면 문법보다 먼저 실행계획을 확인하는 습관을 들이는 것이 좋습니다.
