외부 테이블의 각 튜플마다 내부 테이블을 스캔하여 조건에 맞는 튜플을 찾는 방식.
두 테이블 간의 이중 루프 구조로 작동:
for each row R in outer_table:
for each row S in inner_table:
if R.key = S.key:
output R, S
양 테이블 모두 조인 키 기준으로 정렬되어 있어야 함.
정렬된 데이터를 병합하듯이 순차적으로 조인:
while not end of outer and inner:
if outer.key < inner.key:
advance outer
elif outer.key > inner.key:
advance inner
else:
output all matching pairs
한 테이블(일반적으로 작은 테이블)을 해시 테이블로 구성.
다른 테이블을 스캔하면서 해시 테이블에 매핑하여 일치하는 튜플을 찾음.
1. Build phase: 해시 테이블 생성 (작은 테이블 기준)
2. Probe phase: 큰 테이블을 스캔하며 해시 매칭
| 기준 | 중첩 루프 조인 | 병합 조인 | 해시 조인 |
|---|---|---|---|
| 전제 조건 | 없음 (범용) | 조인 키 정렬 | 해시 버킷 생성 |
| 인덱스 활용 | 유리함 | 일부 경우 가능 | 필요 없음 |
| 성능 (일반적) | 느림 | 빠름 (정렬 전제) | 빠름 (메모리 충분 시) |
| 적합한 상황 | 소규모 + 인덱스 존재 | 대용량 + 정렬 or 인덱스 | 중대규모 + 비인덱스 |
| 메모리 사용 | 적음 | 중간 | 많음 (spill 가능) |
| 구현 복잡도 | 간단 | 중간 | 복잡 |
데이터베이스 옵티마이저는 다음을 고려하여 조인 방식을 결정합니다:
성능 최적화를 위해서는 쿼리 작성 시 조인 대상의 크기, 정렬 여부, 인덱스 존재 여부를 고려하여 의도된 실행 계획을 유도해야 하며, 경우에 따라 **힌트(SQL Hint)**를 이용한 조인 방식 강제도 고려됩니다.
SELECT *
FROM Employees E
JOIN Departments D ON E.dept_id = D.dept_id;
Departments)를 메모리에 해시 테이블로 만듦.Employees를 한 줄씩 보면서, 해당 dept_id가 해시 테이블에 있으면 조인.SELECT *
FROM Orders O
JOIN Customers C ON O.customer_id = C.customer_id;
Orders와 Customers가 customer_id로 정렬되어 있다고 가정.| 항목 | 해시 조인 | 병합 조인 |
|---|---|---|
| 정렬 필요 | 없음 | 정렬 필수 |
| 인덱스 필요 | 없음 | 있으면 좋음 (정렬) |
| 메모리 사용량 | 큼 (해시 테이블) | 중간 (정렬 버퍼) |
| 속도 | 데이터 고르게 분포 시 빠름 | 정렬된 상태면 매우 빠름 |