SQL에서 NOT IN과 NOT EXISTS는 서브쿼리에서 특정 조건을 만족하지 않는 데이터를 필터링하는 데 사용되는 연산자이다. 두 연산자는 비슷한 결과를 낼 수 있지만, 동작 방식과 성능, NULL 값 처리에서 차이점을 보인다.
🧩 NOT IN 연산자
NOT IN 연산자는 서브쿼리 결과에 없는 값을 가진 행을 선택한다. 즉, 메인 쿼리의 특정 컬럼 값이 서브쿼리 결과 집합에 존재하지 않는 경우에 TRUE를 반환한다.
SELECT column1, column2, ...
FROM table_name
WHERE column_name NOT IN (subquery);
장점:
- 구문이 간단하고 직관적이다.
- 비교적 작은 데이터 집합에서 사용하기에 적합하다.
단점:
- 서브쿼리에 NULL 값이 포함될 경우, 전체 쿼리 결과가 NULL이 될 수 있다. 이는 NOT IN 연산자가 NULL 값을 처리하는 방식 때문이다. NOT IN은 내부적으로 AND 연산으로 처리되는데, NULL과의 비교는 항상 UNKNOWN을 반환하므로 결과적으로 어떠한 행도 반환하지 않게 된다.
- 큰 데이터 집합에서는 성능이 저하될 수 있다. NOT IN은 서브쿼리의 결과를 모두 메모리에 로드하여 비교하기 때문에, 서브쿼리 결과가 클수록 비효율적이다.
NULL 값 문제 해결:
NOT IN 사용 시 NULL 값으로 인한 문제를 해결하기 위해 다음과 같은 방법을 사용할 수 있다:
- WHERE절에 IS NOT NULL 조건을 추가하여 서브쿼리 결과에서 NULL 값을 제외한다.
- COALESCE 또는 IFNULL 함수를 사용하여 NULL 값을 다른 값으로 대체한다.
🧩 NOT EXISTS 연산자
NOT EXISTS 연산자는 서브쿼리가 결과를 반환하지 않는 경우에 TRUE를 반환한다. 즉, 메인 쿼리의 특정 행과 관련된 데이터가 서브쿼리에 존재하지 않는 경우에 해당 행을 선택한다.
SELECT column1, column2, ...
FROM table_name
WHERE NOT EXISTS (subquery);
장점:
- NULL 값을 안전하게 처리할 수 있다. NOT EXISTS는 서브쿼리의 결과 존재 여부만 확인하므로 NULL 값에 영향을 받지 않는다.
- 큰 데이터 집합에서 NOT IN보다 성능이 우수할 수 있다. NOT EXISTS는 서브쿼리에서 조건에 맞는 행을 찾으면 즉시 실행을 중단하므로, 불필요한 검색을 줄일 수 있다.
단점:
- NOT IN보다 구문이 복잡하게 느껴질 수 있다.
성능 비교
일반적으로 NOT EXISTS는 NOT IN보다 성능이 더 좋은 경향이 있다. 특히 서브쿼리의 결과 집합이 클 경우, NOT EXISTS는 더 효율적인 실행 계획을 생성할 수 있다. NOT EXISTS는 조건에 맞는 행을 찾는 즉시 서브쿼리 실행을 중단할 수 있지만, NOT IN은 서브쿼리 전체를 실행하여 결과를 메모리에 저장해야 하기 때문이다.
결론
NOT IN과 NOT EXISTS는 모두 유용한 연산자이지만, 다음과 같은 점을 고려하여 상황에 맞게 선택하는 것이 좋다.
- NULL 값: 서브쿼리 결과에 NULL 값이 포함될 가능성이 있다면 NOT EXISTS를 사용하는 것이 안전하다.
- 성능: 큰 데이터 집합에서는 NOT EXISTS가 더 나은 성능을 제공할 수 있다.
- 가독성: NOT IN이 더 직관적으로 느껴진다면, NULL 값 처리 문제를 해결하고 성능을 고려하여 사용 여부를 결정한다.
최적의 성능을 위해서는 쿼리 실행 계획을 분석하고, 필요에 따라 인덱스를 추가하는 등의 튜닝을 수행하는 것이 중요하다.