SQL

PostgreSQL(ANSI SQL): LEFT JOIN ... IS NULL과 NOT EXISTS의 차이

dewstream 2024. 12. 23. 16:00
728x90

※ PostgreSQL: LEFT JOIN ... IS NULL vs NOT EXISTS — What's the Difference?

 

안녕하세요. 듀스트림입니다.

 

오늘은 LEFT JOIN ... IS NULL과 NOT EXISTS는 어떤 차이가 있고 어떻게 동작하는지 더 깊게 알아보겠습니다.

 

차이점을 표로 먼저 보여드리겠습니다.

항목 LEFT JOIN ... IS NULL NOT EXISTS
동작 방식 조인 결과에서 NULL 값을 확인 서브쿼리를 통해 조건 불만족 여부 확인
성능 큰 데이터셋에서 조인으로 데이터 크기 증가 가능 조건이 적은 경우 효율적일 가능성 있음
가독성 비교적 간단하나 큰 데이터셋에서는 복잡해질 수 있음 직관적으로 이해 가능
사용 예시 외부 조인 결과에서 매칭되지 않는 데이터 확인 특정 조건에 맞는 데이터가 존재하지 않을 때

 

결론부터 말씀드리면,

• 작은 데이터 셋에서는 LEFT JOIN ... IS NULL을 사용하시면 되고, 큰 데이터 셋에서는 NOT EXISTS를 사용하는 게 유리합니다.

일반적으로 NOT EXISTS가 옵티마이저에 의해 효율적으로 처리되는 경우가 많습니다.

 

그럼 이제 조금 더 자세히 알아볼까요?


1. LEFT JOIN ... IS NULL

작동 원리

• JOIN ... IS NULL은 LEFT JOIN을 사용한 후 결과에서 NULL 값을 필터링합니다.

 

PostgreSQL 옵티마이저는 다음 단계를 거칩니다.

  1. LEFT JOIN 실행:
    두 테이블을 조인 조건에 따라 결합합니다.
    • 조인 조건에 매칭되지 않는 외부 테이블의 행에 대해 내부 테이블의 값은 NULL로 채워집니다.

  2. NULL 필터링:
    • WHERE 절에서 IS NULL 조건을 적용하여, 조인되지 않은 행만 필터링합니다.
    • 이 단계에서 옵티마이저는 다양한 조인 전략을 사용할 수 있습니다.

 

추가 내용

• 옵티마이저는 테이블 크기, 인덱스 유무, 통계 정보를 바탕으로 Seq Scan, Hash Join, Hash Anti Join, Nested Loop Join 등의 전략을 선택합니다.
• 조인의 크기가 증가하면, 조인 결과의 임시 테이블이 커질 수 있어 메모리 사용량이 증가합니다.
• NULL 필터링은 조인된 결과를 후처리하는 단계에서 작동하기 때문에, 조인 결과가 클 경우 성능에 영향을 미칠 수 있습니다.

• 내부 테이블에 인덱스가 있다면 조인 과정에서 탐색이 더 효율적으로 수행됩니다.

• 조인 결과의 임시 테이블 크기가 크면 메모리 사용량이 증가하고, 디스크 기반 처리로 인해 성능 저하가 발생할 수 있습니다.

• 특히 조인 결과를 필터링하기 전에 임시 테이블이 생성되므로, 외부 테이블(LEFT)에 많은 행이 있으면 부담이 커집니다.

 

2. NOT EXISTS

작동 원리

• NOT EXISTS는 서브쿼리를 통해 조건에 맞는 행이 존재하지 않는지를 확인합니다.

• 외부 쿼리의 각 행에 대해 서브쿼리를 실행하며 다음을 수행:
→ 서브쿼리가 조건을 만족하는 행을 찾으면 즉시 종료.
→ 서브쿼리 조건이 충족되지 않으면 외부 행이 결과에 포함됩니다.

 

PostgreSQL 옵티마이저는 이를 다음과 같이 처리합니다.

  1. 조기 종료 (Short-Circuit Evaluation):
    • 서브쿼리의 조건이 만족되면 즉시 종료하여 불필요한 연산을 줄입니다.
    • 조건을 만족하지 않을 경우에만 전체 탐색이 계속 진행됩니다.

  2. HASH ANTI JOIN 변환:
    • PostgreSQL 9.0 이상 버전에서는 NOT EXISTS가 Hash Anti Join으로 변환될 수 있습니다.
    • Hash Anti Join은 두 테이블 간에 매칭되지 않는 행만 선택하도록 설계되어 성능이 대폭 향상됩니다.
  3. 실행 계획 선택:
    • 옵티마이저는 내부적으로 Seq Scan, Index Scan, Bitmap Heap Scan 중 적합한 스캔 방식을 선택합니다.
    • 서브쿼리와 외부 테이블의 크기, 인덱스 유무, 통계 정보를 고려하여 최적화된 실행 계획을 생성합니다.

  4. 인덱스 활용:
    • 서브쿼리의 조건 열에 인덱스가 있으면 탐색 속도가 크게 향상됩니다.
    • 특히 Nested Loop Join이나 Index Scan을 활용할 때 효과적입니다.

 

+ ANTI JOIN

• PostgreSQL 옵티마이저는 LEFT JOIN ... IS NULL과 NOT EXISTS를 Hash Anti Join으로 변환할 수 있습니다.
• Hash Anti Join은 두 테이블을 비교하며, 조건에 맞지 않는 행만 필터링합니다.
• 이는 모든 행을 개별적으로 처리하지 않고 효율적으로 매칭되지 않는 행을 탐색합니다.

• 외부 테이블이 크고, 내부 테이블이 작을 때 적합합니다.

 

테스트 내용은 아래 포스팅 참고 부탁드립니다.

 

PostgreSQL 쿼리 튜닝: LEFT JOIN ... IS NULL → NOT EXISTS로 변환, 인덱스 활용

※ Transforming LEFT JOIN with NULL Filtering to NOT EXISTS for Better Index Utilization. 안녕하세요. 듀스트림입니다. 오늘 포스팅은 오랜만에 쿼리 튜닝 사례입니다. 이번 사례는 LEFT JOIN ... NULL 필터링을 NOT EXIST

dewstream.tistory.com


그럼 오늘은 여기까지!

다음 포스팅도 기대해 주세요!

 

728x90