본문 바로가기
CS/Database

[MySQL] MySQL의 JOIN, 기본키(PK)

by clolee 2025. 4. 8.

 

✅ 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;

📌 usersorders 테이블에서 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)를 주로 사용하는가?

JOINON 절에서 무엇을 기준으로 조인할 것인가는 성능, 정확도, 의미 있는 데이터 연결 모두에 영향을 준다.


✅ 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은 매우 조심, 정확한 결과를 위해 키 설계에 신경 써야 함

댓글