🎯 학습 목표
- Nested Loops·Hash·Sort Merge 조인의 동작을 구분한다.
- 각 조인이 유리한 상황을 판단한다.
- 조인 순서(드라이빙 테이블)가 왜 중요한지 이해한다.
📖 조인은 “두 테이블을 짝짓는 일”
조인 방식은 옵티마이저가 두 테이블의 행을 어떻게 짝지을지 정하는 알고리즘입니다. 같은 결과라도 방식에 따라 일량이 크게 달라집니다. 오라클의 3대 조인을 봅니다.
📖 1) Nested Loops (중첩 루프 조인)
for 각 행 r1 in (작은 테이블 A) ← 바깥(드라이빙) 테이블
for 매칭 행 r2 in (B를 인덱스로 검색) ← 안쪽 테이블은 조인 컬럼에 인덱스 필수
r1+r2 결과 출력
* A에서 한 건 뽑아 → B에서 인덱스로 매칭 건을 찾는 방식
* 작은 결과 집합 + 안쪽 테이블 조인컬럼에 인덱스가 있을 때 매우 빠름
* OLTP(소량 조회)의 기본 조인. 첫 결과를 빨리 내는 데 유리(부분범위 처리)
핵심: 바깥 테이블은 적게 뽑히고, 안쪽 테이블 조인 컬럼에 인덱스가 있어야 빛납니다. 바깥에서 많은 행이 나오면 안쪽 검색을 그만큼 반복해 느려집니다.
📖 2) Hash Join (해시 조인)
1단계: 작은 테이블 A를 읽어 조인키로 '해시 테이블'을 메모리에 만든다 (Build)
2단계: 큰 테이블 B를 한 번 스캔하며 각 행의 조인키를 해시 테이블에 던져 매칭 (Probe)
* 인덱스 없이도 동작 (조인키 해싱으로 매칭)
* 대량 데이터를 한 번씩만 읽어 짝지음 → 대용량 조인/배치에 강함
* 등호(=) 조인에만 사용 가능. 작은 쪽이 메모리에 들어갈수록 빠름
핵심: 큰 테이블 두 개를 통째로 짝지을 때 최강입니다. 인덱스가 없어도, 각 테이블을 한 번씩만 읽으면 되기 때문입니다.
📖 3) Sort Merge Join (정렬 병합 조인)
1단계: 두 테이블을 각각 조인키로 정렬
2단계: 정렬된 두 줄을 나란히 훑으며 병합(merge)하듯 매칭
* 양쪽 정렬 비용이 있지만, 부등호(>, BETWEEN) 조인이나
이미 정렬된 데이터에 유리
* 해시 조인이 어려운 비등가 조인에서 대안
📖 언제 무엇을 쓰나 — 요약
소량 조회 + 안쪽 인덱스 있음 → Nested Loops (OLTP, 응답시간)
대량 ↔ 대량, 등가 조인 → Hash Join (배치/집계, 처리량)
비등가 조인 / 이미 정렬된 데이터 → Sort Merge
* 잘못된 조인의 전형: 대량×대량인데 Nested Loops로 풀려
안쪽 테이블을 수백만 번 반복 검색 → 매우 느림 → Hash로 유도
💻 조인 방식/순서 힌트 (필요할 때만)
-- 옵티마이저가 잘못된 선택을 할 때 힌트로 교정 (최후의 수단)
SELECT /*+ USE_NL(b) LEADING(a) */ ... -- a를 먼저, b와 NL 조인
FROM a JOIN b ON a.id = b.a_id ...;
SELECT /*+ USE_HASH(a b) */ ... -- 해시 조인 유도
📖 조인 순서: 드라이빙 테이블이 성능을 가른다
여러 테이블을 조인할 때 “먼저 읽어 행을 적게 만드는 테이블”(드라이빙 테이블)을 앞에 두는 것이 핵심입니다. 첫 테이블에서 행을 많이 만들면 뒤 단계 전체가 그 행 수만큼 부풀려집니다. 가장 강하게 필터링되는(=적게 남는) 테이블을 먼저 처리하면 전체 일량이 줄어듭니다. 옵티마이저는 통계로 이를 추정하므로(6강), 통계가 정확해야 올바른 순서를 고릅니다.
⚠️ 주의사항
- 힌트는 옵티마이저를 못 믿을 때의 마지막 수단입니다. 힌트를 남발하면 데이터가 바뀌어도 계획이 고정돼 나중에 독이 됩니다. 먼저 통계·인덱스를 바로잡는 게 우선입니다.
- Hash Join용 메모리(PGA)가 부족하면 디스크로 넘쳐(temp) 느려집니다. 대량 해시 조인이 느리면 temp 사용 여부를 확인하세요.
💡 팁
- 실행계획에서 조인 방식을 먼저 확인하세요. “대량 데이터인데 NESTED LOOPS”거나 “소량인데 불필요한 HASH”면 통계/인덱스를 점검할 신호입니다.
- 왜 그 순서/방식이 선택됐는지는 결국 옵티마이저의 행 수 추정(통계)에 달려 있습니다 → 다음 강(6강).