[Database] SQL 서브쿼리: IN vs EXISTS

🚀 이 주제를 선택한 이유 & 학습 목표

  • 선택 배경: WHERE 절에서 특정 조건을 만족하는 데이터 집합을 필터링하기 위해 서브쿼리를 사용하던 중, IN으로는 되던 것이 EXISTS로는 왜 안되는지 궁금해졌습니다. 두 연산자의 근본적인 동작 방식의 차이를 명확히 이해하고 싶었습니다.
  • 학습 목표:
    1. SQL 서브쿼리의 기본 개념과 종류(상관/비상관)를 이해한다.
    2. IN 연산자와 EXISTS 연산자의 동작 방식을 비교하고 차이점을 설명할 수 있게 된다.
    3. 각 연산자가 언제 더 효율적인지 판단하고, 상황에 맞게 올바르게 사용할 수 있는 능력을 기른다.

📚 핵심 개념 및 원리

1. 주요 용어 정의

  • 서브쿼리 (Subquery): 다른 SQL 쿼리 안에 포함된 SELECT 문. 메인 쿼리에 값을 제공하거나 필터링 조건을 설정하는 데 사용됩니다.
  • 비상관 서브쿼리 (Non-correlated Subquery): 메인 쿼리와 독립적으로 한 번만 실행되어 그 결과를 메인 쿼리에 전달하는 서브쿼리입니다.
  • 상관 서브쿼리 (Correlated Subquery): 메인 쿼리의 컬럼 값을 참조하여, 메인 쿼리의 각 행마다 반복적으로 실행되는 서브쿼리입니다[75].
  • IN 연산자: 특정 컬럼의 값이 서브쿼리가 반환한 '결과 목록'에 포함되는지 확인합니다[79].
  • EXISTS 연산자: 서브쿼리가 '결과 행을 하나라도 반환하는지'의 존재 여부(True/False)만 확인합니다[76].

2. 핵심 원리/동작 방식

시나리오: "팁(tip)의 일일 합계가 1500 이상인 날에 발생한 모든 팁 내역 조회"

IN 사용 (비상관 서브쿼리)

IN은 '서브쿼리 -> 메인 쿼리' 순서로 동작합니다[79].

  1. 서브쿼리 실행: 먼저 IN 안의 서브쿼리가 독립적으로 실행되어 tip 합계가 1500 이상인 day 목록 (예: ['Sat', 'Sun'])을 메모리에 만듭니다.
  2. 메인 쿼리 실행: 메인 쿼리가 tips 테이블의 모든 행을 스캔하면서 day 컬럼의 값이 위에서 만든 목록(['Sat', 'Sun'])에 포함되는지 비교하여 필터링합니다.

EXISTS 사용 (상관 서브쿼리)

EXISTS는 '메인 쿼리 -> 서브쿼리' 순서로 동작하며, 메인 쿼리와 서브쿼리가 관계를 맺어야 합니다[80].

  1. 메인 쿼리 스캔: 메인 쿼리가 tips 테이블의 첫 번째 행부터 한 행씩 읽습니다.
  2. 상관 서브쿼리 실행: 읽어온 행의 day 값을 가지고 서브쿼리를 실행합니다. 서브쿼리는 해당 daytip 합계가 1500 이상인지 확인합니다.
  3. 존재 여부 판단: 서브쿼리가 조건을 만족하여 한 행이라도 결과를 반환하면 EXISTSTrue가 되고, 해당 메인 쿼리 행은 결과에 포함됩니다. 이 과정을 모든 행에 대해 반복합니다.

💡 장점, 단점 및 기술 비교

특징 IN EXISTS
처리 방향 서브쿼리 → 메인 쿼리 메인 쿼리 → 서브쿼리
핵심 동작 값의 목록 비교 (List에 포함 여부) 조건 만족 행의 존재 여부 확인 (True/False)
서브쿼리 유형 주로 비상관 서브쿼리 반드시 상관 서브쿼리로 구성해야 함
성능 (일반적) 서브쿼리 결과 집합이 작을 때 유리 메인 쿼리 테이블이 매우 크고, 서브쿼리 조건에 인덱스가 있을 때 유리
NULL 처리 서브쿼리 결과에 NULL이 있으면 예기치 않은 동작 발생 가능 NULL 값의 영향을 받지 않음
가독성 직관적이고 이해하기 쉬움 구조가 복잡해질 수 있음

🤔 나의 이해와 생각 정리 (회고)

  • 핵심 요약:

    • IN은 서브쿼리가 만든 결과 목록을 가지고 필터링하는 '값 기반' 연산이다.
    • EXISTS는 메인 쿼리의 각 행이 서브쿼리의 조건을 만족하는지 확인하는 '논리 기반' 연산이다.
    • 따라서, EXISTS를 쓸 때는 WHERE t1.id = t2.id 와 같이 메인 쿼리와 서브쿼리 간의 '상관관계'를 명시해주어야만 의도대로 동작한다.
  • 새롭게 깨달은 점: EXISTS가 비상관 서브쿼리와 함께 쓰일 때 왜 전체 데이터가 나오거나 아무것도 안 나오는지 명확히 이해했다. 서브쿼리가 메인 쿼리와 무관하게 딱 한 번 실행되어 항상 True 또는 항상 False가 되기 때문이었다. EXISTS는 그 자체로 필터링 도구가 아니라, 상관관계를 통해 각 행을 검증하는 '스위치' 역할을 한다는 것을 깨달았다.

  • 더 궁금해진 점 / 의문점:

    • JOIN으로 동일한 결과를 낼 수 있는 경우가 많은데, IN, EXISTS, JOIN 사이의 성능 차이는 구체적으로 어떤 상황에서 발생하는가?
    • DB 옵티마이저가 IN 쿼리를 내부적으로 EXISTSJOIN으로 변환하여 실행하는 경우도 있을까?

📖 더 학습할 내용 및 참고 자료

  • 추가 학습 희망 분야:
    • SQL 실행 계획(Execution Plan) 분석 방법
    • IN vs EXISTS vs JOIN 성능 벤치마킹
    • NOT IN vs NOT EXISTS의 차이점 및 NULL 처리 문제

✨ 마무리하며

단순히 'A는 되고 B는 안된다'에서 그치지 않고, 두 연산자의 근본적인 작동 철학의 차이를 파고드니 SQL 쿼리를 더 깊이 있게 이해하게 되었다. 앞으로는 쿼리 작성 시 '어떻게 동작할까?'를 먼저 생각하고, 데이터의 크기와 구조를 고려하여 더 효율적인 방법을 선택하는 습관을 들여야겠다.

'Study > SQL' 카테고리의 다른 글

[SQL]SQL 집계 및 그룹화  (0) 2025.09.17