콘텐츠로 이동

2026 01 05

2026-01-05

Semi-Join & Exists

  • 개념
    • 두 테이블을 조인할 때, 데이터를 실제로 합치는 것이 아닌, "존재 여부만 확인" 하는 기법
    • 데이터를 가져올 필요 없이 있는지만 궁금할 때에는 EXISTS가 훨씬 빠르다. MySQL은 Semi-Join으로 이를 해결
    • 중복 제거 자동 적용
  • Semi-Join (반쪽 조인)
    • 일반적인 Inner Join은 교집합 데이터를 모두 만듦
    • Semi-Join은 "Outer 테이블의 행이 Inner 테이블에 존재하는가"만 판단
      • 판단 후 즉히 Outer 테이블의 행으로 넘어감
    • 왼쪽 테이블에서 오른쪽 테이블과 매칭되는 행만 반환 + 오른쪽 테이블의 데이터는 가져오지 않음
  • 문제
    • JOIN + GROUP BY는, 데이터를 모두 펼쳐놓고 조인하고, 다시 묶어서 그룹핑 갯수를 세기에 메모리를 많이 먹고 느림
  • 개선
    • MySQL은 IN 혹은 EXISTS 서브 쿼리를 만났을 때, 내부적으로 Semi-Join 최적화 진행
    • FirstMatch, Materialization, Duplicate Weedout
  • 예시
    • Users: 10,000명
    • Orders: 1,000,000건
    • 한 번이라도 주문한 적 있는 회원 목록 추출
      • [Bad Case]

        SELECT u.user_id, u.name
        FROM Users u
        JOIN Orders o ON u.user_id = o.user_id
        GROUP BY u.user_id;
        

        • 회원 A가 주문했는지는 첫번째만 봐도 아는데, 굳이 100번 가량 조인 - [Good Case]
          SELECT u.user_id, u.name
          FROM Users u
          WHERE EXISTS (
            SELECT 1
            FROM Orders o
            WHERE o.user_id = u.user_id
          )
          
        • Outer Loop: Users 테이블에서 회원 A가져옴
        • Inner Loop: Orders 테이블의 인덱스 타고 회원 A의 주문을 찾음
        • Short-circuit: 주문 내역 하나라도 발견시 (First Match), 회원 A의 주문 찾지 않고 멈춤
  • MySQL 옵티마이저
    • MySQL 8.0 이후 부터는 IN, EXIST 쿼리 작성시, 옵티마이저가 자동으로 Semi-Join 최적화 수행함

스칼라 서브쿼리

  • 서브쿼리의 결과가 단일 값으로 나오는 쿼리. WHERE 절 안에서 특정 갯수가 값을 비교할 때 사용하는, 결과가 딱 하나만 나오는 미니 쿼리
  • 각 유저 행마다 실행되지만, 인덱스를 타면 매우 빠름
  • SELECT 절이나, WHERE 절에서 변수처럼 비교 가능

Correlated Subquery & attached_subqueries

  • 메인 쿼리의 컬럼을 참조하는 서브쿼리
  • 메인 쿼리의 각 행마다 붙어서 실행되는 조건을 의미
  • 상관 서브쿼리는 느리다는 오해가 있지만, 최신 DB Optimizer는 인덱스 Lookup을 통해 효율적으로 처리하도록 진화

eq_ref (Primary Key Lookup)

  • MySQL 실행계획 EXPLAIN type 중 하나 (eq_ref)
  • Join 시, Primary Key/Unique Key를 사용해 단 1건의 데이터만 조회한다는 뜻
  • 예시)
    • 현재 enrollment 테이블의 PK는 (user_id, company_id)
    • 서브쿼리 돌릴 필요 없이, PK로 콕 찍어서 확인 (eq_ref)
      AND EXISTS (
          SELECT 1
          FROM enrollment
          WHERE user_id = x.id
              AND company_id = 1
      )
      

쿼리 최적화 여정

  • Short-Circuit Evaluation
    • 앞의 조건이 False면 뒤의 조건은 실행하지 않도록
    • EXISTS (조건A) AND EXISTS (조건B)
  • 인덱스 explain type
    1. system/const: 테이블에 데이터 1건이거나 상수로 PK 조회하는 경우
    2. eq_ref: 테이블 조인할 때 딱 PK/Unique Key로 1건만 읽음
    3. ref: 인덱스를 타지만, 결과 여러개일 수 있음
    4. range: 범위 검색
    5. index: 인덱스 전체 스캔
    6. ALL: 테이블 전체 스캔