SQL JOIN 완벽 가이드
소개
SQL JOIN은 데이터베이스 실무에서 가장 자주 쓰는 문법이지만, 종류와 동작을 헷갈리면 잘못된 결과나 느린 쿼리가 나오기 쉽습니다. 우리도 리워드·이벤트 데이터를 다루면서 JOIN 실수와 성능 이슈를 여러 번 겪었고, 그때 정리한 “실무에서 바로 쓸 수 있는” 예제와 주의사항, 최적화 팁을 모아 두었습니다.
1. JOIN의 종류와 차이
| JOIN 종류 | 설명 |
|---|---|
| INNER JOIN | 두 테이블 모두에 존재하는 데이터만 반환 |
| LEFT (OUTER) JOIN | 왼쪽 테이블의 모든 데이터 + 매칭되는 오른쪽 |
| RIGHT (OUTER) JOIN | 오른쪽 테이블의 모든 데이터 + 매칭되는 왼쪽 |
| FULL OUTER JOIN | 양쪽 테이블의 모든 데이터(매칭 여부 무관) |
| CROSS JOIN | 두 테이블의 모든 조합(카티션 곱) |
2. 실무 예제 테이블
1-- 직원 테이블
2CREATE TABLE employees (
3 id INT PRIMARY KEY,
4 name VARCHAR(50),
5 dept_id INT
6);
7
8-- 부서 테이블
9CREATE TABLE departments (
10 id INT PRIMARY KEY,
11 dept_name VARCHAR(50)
12);
13
14-- 데이터 입력
15INSERT INTO employees VALUES
16 (1, '홍길동', 10),
17 (2, '이영희', 20),
18 (3, '김철수', NULL);
19
20INSERT INTO departments VALUES
21 (10, '개발'),
22 (20, '마케팅'),
23 (30, '영업');3. JOIN별 실전 쿼리 & 결과
1) INNER JOIN
1SELECT e.name, d.dept_name
2FROM employees e
3INNER JOIN departments d
4 ON e.dept_id = d.id;설명: 두 테이블 모두에 매칭되는 데이터만 반환
| name | dept_name |
|---|---|
| 홍길동 | 개발 |
| 이영희 | 마케팅 |
2) LEFT JOIN
1SELECT e.name, d.dept_name
2FROM employees e
3LEFT JOIN departments d
4 ON e.dept_id = d.id;설명: 직원 전체 + 부서가 없는 직원도 포함 (NULL)
| name | dept_name |
|---|---|
| 홍길동 | 개발 |
| 이영희 | 마케팅 |
| 김철수 | NULL |
3) RIGHT JOIN
1SELECT e.name, d.dept_name
2FROM employees e
3RIGHT JOIN departments d
4 ON e.dept_id = d.id;설명: 부서 전체 + 소속 직원이 없는 부서도 포함 (NULL)
| name | dept_name |
|---|---|
| 홍길동 | 개발 |
| 이영희 | 마케팅 |
| NULL | 영업 |
4) FULL OUTER JOIN (DBMS 지원 시)
1SELECT e.name, d.dept_name
2FROM employees e
3FULL OUTER JOIN departments d
4 ON e.dept_id = d.id;설명: 직원, 부서 모두 매칭 여부와 상관없이 전체 반환
| name | dept_name |
|---|---|
| 홍길동 | 개발 |
| 이영희 | 마케팅 |
| 김철수 | NULL |
| NULL | 영업 |
5) CROSS JOIN
1SELECT e.name, d.dept_name
2FROM employees e
3CROSS JOIN departments d;설명: 모든 직원-부서 조합 (카티션 곱)
| name | dept_name |
|---|---|
| 홍길동 | 개발 |
| 홍길동 | 마케팅 |
| 홍길동 | 영업 |
| 이영희 | 개발 |
| 이영희 | 마케팅 |
| 이영희 | 영업 |
| 김철수 | 개발 |
| 김철수 | 마케팅 |
| 김철수 | 영업 |
4. 실무 체크리스트
💡 JOIN 쿼리 작성 시 반드시 확인해야 할 사항들입니다.
- JOIN 조건(ON 절)을 빠뜨리지 않았는가?
- NULL 값 처리(LEFT/RIGHT JOIN 결과) 방안을 마련했는가?
- 불필요한 CROSS JOIN을 사용하지 않았는가?
- 조인 대상 컬럼에 인덱스가 있는가?
- 결과 데이터가 예상과 일치하는지 쿼리 결과를 검증했는가?
5. 실무 주의사항 & 실패사례
⚠️ 실제 프로젝트에서 자주 발생하는 문제점들입니다.
주의사항
- ON 조건 없이 JOIN을 사용해, 의도치 않은 카티션 곱(수십만~수백만 건)이 발생한 사례가 많습니다.
- LEFT JOIN 결과의 NULL을 제대로 처리하지 않아, 통계/리포트에서 누락/오류가 발생할 수 있습니다.
- 대용량 테이블 조인 시 인덱스가 없으면 쿼리 속도가 급격히 저하됩니다.
- DBMS마다 FULL OUTER JOIN 지원 여부가 다르니, 미지원 시 UNION 등으로 대체해야 합니다.
성능 최적화 팁
- 조인 컬럼에 인덱스 추가
- 불필요한 컬럼 제외
- 서브쿼리 대신 CTE 사용
- 적절한 조인 순서 선택
6. 자주 묻는 질문(FAQ)
Q. LEFT JOIN과 RIGHT JOIN, 언제 써야 하나요?
💡 둘 다 결과는 비슷하지만, 기준이 되는(모든 행을 포함할) 테이블이 왼쪽이면 LEFT, 오른쪽이면 RIGHT를 사용합니다.
Q. FULL OUTER JOIN이 없는 DB에서는 어떻게 하나요?
1SELECT e.name, d.dept_name
2FROM employees e
3LEFT JOIN departments d
4 ON e.dept_id = d.id
5UNION
6SELECT e.name, d.dept_name
7FROM employees e
8RIGHT JOIN departments d
9 ON e.dept_id = d.id;Q. JOIN이 느릴 때 어떻게 최적화하나요?
💡 성능 최적화를 위한 핵심 전략
- 조인 컬럼에 인덱스를 추가하고
- 불필요한 컬럼/행은 쿼리에서 제외하세요
- 서브쿼리 대신 WITH절(CTE)도 활용해보세요
실전 성능 최적화 팁
인덱스 활용
1-- JOIN 조건에 사용되는 컬럼에 인덱스 생성
2CREATE INDEX idx_employees_dept_id ON employees(dept_id);
3CREATE INDEX idx_orders_user_id ON orders(user_id);
4
5-- 복합 인덱스 (WHERE와 JOIN 조건 모두 고려)
6CREATE INDEX idx_orders_user_status ON orders(user_id, status);EXPLAIN으로 쿼리 분석
1-- 쿼리 실행 계획 확인
2EXPLAIN ANALYZE
3SELECT e.name, d.dept_name
4FROM employees e
5INNER JOIN departments d ON e.dept_id = d.id
6WHERE e.salary > 50000;서브쿼리 vs JOIN
1-- 나쁜 예: 서브쿼리 사용
2SELECT name
3FROM employees
4WHERE dept_id IN (
5 SELECT id FROM departments WHERE dept_name = 'Engineering'
6);
7
8-- 좋은 예: JOIN 사용 (일반적으로 더 빠름)
9SELECT e.name
10FROM employees e
11INNER JOIN departments d ON e.dept_id = d.id
12WHERE d.dept_name = 'Engineering';실무 체크리스트
JOIN을 사용할 때 다음 사항을 확인하세요:
- JOIN 조건에 인덱스가 있는가?
- NULL 값 처리가 올바른가?
- 중복 데이터가 발생하지 않는가?
- 필요한 데이터만 SELECT하고 있는가?
- WHERE 조건이 JOIN 전에 적용되는가?
- 쿼리 실행 계획을 확인했는가?
7. 참고자료
📚 더 깊이 학습하기 위한 추천 자료