✅ MySQL의 JOIN 완전 정복 + FULL OUTER JOIN 대체 방법까지
🧠 1. JOIN이란?
- 두 개 이상의 테이블을 공통된 컬럼을 기준으로 연결해 하나의 결과 집합을 만드는 방식
- 실무에서 매우 흔하게 사용됨: 사용자 + 주문, 제품 + 카테고리, 회원 + 주소 등
✅ 2. JOIN 종류 요약
JOIN 종류 | 설명 | MySQL 지원 여부 |
---|---|---|
INNER JOIN | 두 테이블 모두에 존재하는 교집합 | ✅ 지원 |
LEFT OUTER JOIN (LEFT JOIN) | 왼쪽 테이블은 모두 표시, 오른쪽은 일치하는 것만 | ✅ 지원 |
RIGHT OUTER JOIN (RIGHT JOIN) | 오른쪽 테이블은 모두 표시, 왼쪽은 일치하는 것만 | ✅ 지원 |
FULL OUTER JOIN | 양쪽 모두 표시 (합집합) | ❌ MySQL은 직접 지원 안 함 |
CROSS JOIN | 모든 조합 (곱집합) | ✅ 지원 |
SELF JOIN | 자기 자신과 조인 | ✅ 지원 |
✅ 3. INNER JOIN
🔍 개념
- 두 테이블에서 공통된 키 값이 있는 경우만 결과에 포함됨 (교집합)
✅ 예시
SELECT u.name, o.order_date
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
📌 users
와 orders
테이블에서 id = user_id
인 경우만 조회됨.
✅ 4. LEFT OUTER JOIN
🔍 개념
- 왼쪽 테이블의 모든 행을 보존, 오른쪽 테이블은 일치하는 행만 표시
- 오른쪽에 일치하는 데이터가 없으면
NULL
✅ 예시
SELECT u.name, o.order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
📌 주문하지 않은 유저도 포함되며, 주문 내역은 NULL로 표시됨.
✅ 5. RIGHT OUTER JOIN
🔍 개념
- 오른쪽 테이블의 모든 행을 보존, 왼쪽은 일치하는 것만
- 실무에서는
LEFT JOIN
으로 구조를 바꾸는 경우가 많음
✅ 예시
SELECT u.name, o.order_date
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
📌 주문이 있는데 사용자 정보가 누락된 경우도 보여줄 수 있음 (비정상 데이터 탐색 시 사용)
✅ 6. FULL OUTER JOIN (MySQL 미지원)
🔍 개념
- 두 테이블 모두에서 일치하거나 일치하지 않는 모든 행을 반환
- 즉, LEFT JOIN + RIGHT JOIN의 합집합
❌ MySQL은 FULL OUTER JOIN을 지원하지 않음!
✅ 대체 방법: UNION
또는 UNION ALL
로 구현
-- FULL OUTER JOIN 대체 패턴 (MySQL)
SELECT u.name, o.order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
UNION
SELECT u.name, o.order_date
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
📌 이 방식은:
LEFT JOIN
으로 왼쪽 우선 매칭RIGHT JOIN
으로 오른쪽 독립 데이터도 포함UNION
: 중복 제거 (정렬 발생, 느릴 수 있음)UNION ALL
: 중복 제거 안 함 (속도 빠름)
📌 FULL OUTER JOIN 실전 예제
-- 가입은 했지만 주문 안 한 유저 + 주문했지만 사용자 정보 없는 데이터 모두 포함
SELECT u.id AS user_id, u.name, o.order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
UNION
SELECT u.id AS user_id, u.name, o.order_date
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
💡 중복 행이 생길 수 있으므로, DISTINCT
또는 고유한 키를 잘 다뤄야 함
✅ 7. CROSS JOIN
🔍 개념
- 모든 조합 (Cartesian Product)
- 예: A(2행) × B(3행) = 결과 6행
✅ 예시
SELECT *
FROM colors c
CROSS JOIN sizes s;
📌 색상 × 사이즈 조합을 모두 출력할 때 유용
✅ 8. SELF JOIN
🔍 개념
- 같은 테이블을 자기 자신과 조인
- 계층 구조, 매니저-직원 관계 등에서 사용
✅ 예시
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
📌 같은 테이블을 두 번 불러서 직원-매니저 관계
표현
✅ 9. 실무 조인 팁 요약
상황 | 추천 JOIN 방식 | 설명 |
---|---|---|
사용자 + 주문 | LEFT JOIN |
모든 사용자 포함, 주문 없으면 NULL |
주문 + 제품 | INNER JOIN |
주문과 제품 모두 필수 |
가입은 했지만 활동 없는 유저 포함 | LEFT JOIN |
활동 기록 없어도 포함 |
FULL OUTER 필요 | LEFT JOIN UNION RIGHT JOIN |
MySQL 한계 회피 |
계층 구조 | SELF JOIN |
조직도, 카테고리 등 |
✅ 10. 성능 팁
- ON 절 조건이 빠지면 CROSS JOIN 발생 → 조인 폭발주의
- WHERE 조건은 JOIN 조건과 별도로 정확히 분리
- 필요한 컬럼만 SELECT해서 I/O 줄이기
- 인덱스가 걸린 컬럼으로 조인하는 것이 좋음 (
EXPLAIN
으로 확인)
✅ JOIN의 ON 절은 왜 기본키(PK)를 주로 사용하는가?
JOIN
의 ON
절에서 무엇을 기준으로 조인할 것인가는 성능, 정확도, 의미 있는 데이터 연결 모두에 영향을 준다.
✅ JOIN의 ON 절은 왜 기본키(PK)를 주로 사용하는가?
📌 1. 기본키(PK)란?
- 테이블에서 행(row)을 고유하게 식별하는 컬럼
- 중복 불가 + NULL 불가
- 보통
id
,user_id
,order_id
같은 컬럼이 PK
예:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100)
);
✅ 2. ON 절에서 기본키(PK) = 외래키(FK) 패턴이 가장 흔한 이유
SELECT *
FROM users u
JOIN orders o ON u.id = o.user_id;
테이블 | 역할 |
---|---|
u.id |
기본키 (users의 고유 식별자) |
o.user_id |
외래키 (orders의 참조 키) |
📌 이 구조는 다음의 3가지 장점 때문에 실무에서 사실상 표준.
✅ 장점 1: 무결성(데이터 신뢰성) 확보
- 외래키가 존재하는 경우, 반드시 부모 테이블(PK)에 해당 값이 존재
- 즉, 잘못된 참조(없는 유저 ID로 주문 저장 등)를 방지할 수 있음
✅ 장점 2: 정확한 매칭
- 기본키는 1개 행만 반환되므로 조인 시 중복 매칭 위험이 없음
- 예:
user_id = 101
이면 users 테이블에서 정확히 1행
✅ 장점 3: 성능 최적화
- PK는 자동 인덱스 적용됨 ⇒ 빠른 조인 가능
- FK 컬럼에도 인덱스를 걸면 양방향 모두 성능 향상
✅ 3. ON 절에서 꼭 PK만 써야 하는 건 아님
JOIN은 PK = FK 구조가 가장 안정적이지만,
현실에선 다음과 같은 예외/응용 상황도 많음.
📍 예외 1: 조인 대상이 고정 코드 테이블일 때
SELECT u.name, g.name AS gender_name
FROM users u
JOIN gender_codes g ON u.gender = g.code;
gender_codes.code
는 PK이지만,users.gender
는 단순 문자열 필드 ('M'
,'F'
등)
🔍 PK = 일반 컬럼 형태이지만 여전히 안정적
📍 예외 2: 다대다 조인 (중간 매핑 테이블)
-- students(id), courses(id), student_course(student_id, course_id)
SELECT s.name, c.title
FROM student_course sc
JOIN students s ON sc.student_id = s.id
JOIN courses c ON sc.course_id = c.id;
- 조인 조건은
PK = FK
이지만 중간 매핑 테이블이 조인 핵심 - 여기서도 결국 각 조인은 PK ↔ FK 구조를 유지
📍 예외 3: 조건부/범위 JOIN (비등가 조인)
SELECT e.name, r.level
FROM employees e
JOIN ranks r ON e.salary BETWEEN r.min_salary AND r.max_salary;
- 이건 등가(=) 조인이 아닌 범위 조건 조인
- 실무에서는 급여 등급, 나이대 구간에서 자주 사용
⚠️ 성능 주의: 범위 조인은 인덱스 활용이 어려움
✅ 4. ON 절에서 조인 키를 잘못 쓰면 발생하는 문제
❌ 문제 1: 중복 매칭 → Cartesian product 발생
-- 실수: 조건 누락
SELECT * FROM users u JOIN orders o;
ON
이 없으면 CROSS JOIN 발생 (모든 조합)- 100명 × 1000건 → 10만 행 😱
❌ 문제 2: 비고유 키로 조인 → 중복 결과
-- 이름으로 조인 (비추천)
SELECT * FROM employees e
JOIN projects p ON e.name = p.leader_name;
- 이름이 중복될 수 있어 의도치 않은 매칭 발생
✅ 해결: 고유한 식별자(PK)를 기준으로 조인
✅ 5. 실무에서 ON 절 설정 시 체크리스트
항목 | 체크 포인트 |
---|---|
✔️ 키 일치 여부 | PK ↔ FK 또는 고유 식별자 간 조인 |
✔️ 데이터 타입 일치 | INT = INT, VARCHAR = VARCHAR |
✔️ NULL 여부 확인 | JOIN 키는 NULL 허용 안 되는 게 일반적 |
✔️ 중복 키 방지 | 비고유 컬럼 조인은 조심 |
✔️ 인덱스 존재 확인 | 조인 키 양쪽에 인덱스 존재 여부 확인 (EXPLAIN ) |
✅ 6. 결론 요약
- JOIN의 ON 절은 가능한 한 "기본키 = 외래키" 구조로 조인하는 것이 이상적
- 이유: 무결성 보장 + 정확한 매칭 + 성능 향상
- 예외적으로는 코드 테이블, 매핑 테이블, 범위 조인 등 다양한 변형 가능
- 비고유 컬럼으로 JOIN은 매우 조심, 정확한 결과를 위해 키 설계에 신경 써야 함
'CS > Database' 카테고리의 다른 글
[MySQL] SubQuery 사용 (0) | 2025.04.13 |
---|---|
[MySQL] GROUP BY / HAVING 관련 SELECT 컬럼 정리 + 인라인 뷰 (inline view) 활용법 (0) | 2025.04.13 |
[MySQL] MySQL의 `SELECT` 문에 나올 수 있는 요소들 (0) | 2025.04.04 |
[Database] FROM DUAL 구문 (0) | 2025.04.03 |
[MySQL] SELECT 절의 처리 순서 (0) | 2025.04.01 |
댓글