SELECT *
FROM person p
WHERE p.id NOT IN ( ... )
SQL
복사
NOT IN 쿼리는 부정 포함 조건절로 검색 시 제외할 때 사용 된다.
그런데, 이 부정 조건은 대부분의 DBMS에서 전체 데이터나 테이블을 스캔한 후 조건에 맞지 않는 레코드를 필터링 하기 때문에 테이블 풀스캔을 유발하고 이는 슬로우 쿼리가 될 여지를 준다.
그럼 이 NOT IN 쿼리의 단점은 무엇이 있을까?
NOT IN 쿼리의 문제점
1. 범위를 명확히 정할 수 없다.
IN 절은 특정 값의 목록을 의미한다. 그렇기에 옵티마이저는 “이 값들만 보면 된다”라고판단해 인덱스 탐색을 쉽게 할 수 있다.
하지만, NOT IN 절은 “이 값들을 제외한 모든 값”을 의미하기 때문에 데이터 풀스캔을 하는 경우가 많다.
그리고 이러한 테이블 풀스캔은 성능저하의 요인 중 하나이다.
2. NULL 처리에 문제가 있다.
SELECT *
FROM person p
WHERE p.id NOT IN ( SELECT a.id FROM address a)
SQL
복사
3. 범위 연산을 사용할 수 없다.
인덱스는 일반적으로 범위 조건(ex: BETWEEN, <, > 등)에 유리하다.
하지만 NOT IN은 여러 개의 != 조건을 합친 것과 비슷한 구조라 범위 필터링이 어렵다.
최적화 방안
1. NOT EXISTS 사용
SELECT *
FROM person p
WHERE NOT EXISTS (
SELECT 1 FROM person temp
WHERE temp.id = p.id and temp.id IN ( ... )
)
SQL
복사
NOT EXISTS 는 행 단위로 평가되어 매칭되는 첫 행을 찾으면 즉시 종료된다. 이는 DBMS가 존재하지 않음을 확인하기 위해 최적화된 방식으로 대규모 데이터셋에서 안정적이고 확장성있는 성능을 제공한다.
2. LEFT JOIN + IS NULL 패턴
SELECT *
FROM person p
LEFT JOIN (
SELECT temp.id FROM person temp WHERE temp.id IN (...)
) filtered_person ON p.id = filterd_person.id
WHERE filtered.id IS NULL
SQL
복사
서브쿼리의 데이터가 적을수록 인덱스를 더 효율적으로 사용한다.