F-LAB JAVA · 7주차 · Phase 1 · SQL JOIN: 관계형 DB의 본질
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
FULL OUTER JOIN 은 양쪽 테이블의 모든 행을 반환하고 매칭 없는 쪽은 NULL 로 채워 Charlie 와 Sales 모두 결과에 등장하는 합집합 연산이며, MySQL 은 이를 지원하지 않아 LEFT JOIN UNION RIGHT JOIN 으로 우회하고 INNER ⊂ LEFT ∪ RIGHT = FULL 이라는 포함 관계가 4가지 JOIN 의 본질이다.
FULL OUTER JOIN 은 양쪽 테이블의 모든 행을 반환 하는 합집합 연산으로, LEFT 와 RIGHT 의 효과를 동시에 갖는다.
매칭 없는 쪽은 NULL 로 채워져서 — Charlie (오른쪽 매칭 X) 의 dept_name 은 NULL, Sales (왼쪽 매칭 X) 의 직원 정보는 NULL 인 채로 모두 결과에 포함된다.
MySQL 은 FULL OUTER JOIN 을 지원하지 않으므로LEFT JOIN ... UNION ... RIGHT JOIN으로 우회하며 (PostgreSQL/Oracle/SQL Server 는 지원), 실무에선 자주 쓰지 않고 데이터 정합성 검증·차집합 양방향 확인에 사용된다.
4가지 JOIN 의 결과 포함 관계는 — INNER ⊂ LEFT ⊂ FULL, INNER ⊂ RIGHT ⊂ FULL, LEFT ∪ RIGHT = FULL — 이 벤다이어그램이 JOIN 의 본질이다.
FULL OUTER JOIN = 합동 명단:
상황:
- 학급 A 명단
- 학급 B 명단
- 일부는 두 학급 (전학생)
INNER JOIN: 양쪽 학급 모두 등록된 학생
LEFT JOIN: A 학급 전체 (B 등록 정보는 있으면)
RIGHT JOIN: B 학급 전체
FULL OUTER JOIN: 양쪽 모든 학생
- A 만: B 정보 NULL
- B 만: A 정보 NULL
- 양쪽: 모두 채움
결과:
- Alice (A만): A=Y, B=NULL
- Bob (양쪽): A=Y, B=Y
- Charlie (B만): A=NULL, B=Y
MySQL 없음:
- LEFT + RIGHT 합치기 (UNION)
- 우회
ILIC 의 정합성 검증:
- shipments_local vs shipments_remote
- 양쪽 다 봐서 차이 찾기
4가지 JOIN 관계:
- INNER (교집합)
⊂ LEFT (A 전체)
⊂ FULL (양쪽 전체)
- INNER
⊂ RIGHT (B 전체)
⊂ FULL
- LEFT ∪ RIGHT = FULL
→ FULL OUTER = 양쪽 모두 (합집합), MySQL 우회 (UNION), 4가지 JOIN 의 정점.
1. FULL OUTER JOIN 정의
2. 합집합의 의미
3. Charlie + Sales 동시 등장
4. MySQL 의 지원 X
5. LEFT UNION RIGHT 우회
6. 4가지 JOIN 포함 관계
7. 행 수 비교
8. FULL OUTER 의 실무 활용
9. 면접 + 자기 점검
FULL OUTER JOIN:
양쪽 테이블의 모든 행:
- 양쪽 매칭 시: 합침
- 한쪽만 있으면: 다른 쪽 NULL
- 합집합
키워드:
FULL OUTER JOIN
= FULL JOIN (OUTER 생략 가능)
의미:
- "FULL" = 양쪽 전체
- LEFT + RIGHT 효과
지원 DB:
지원 O:
- PostgreSQL
- Oracle
- SQL Server
- SQLite
지원 X:
- MySQL (우회 필요)
- MariaDB
-- FULL OUTER JOIN (PostgreSQL 가정)
-- 모든 배송 + 모든 고객 (양쪽 보존)
SELECT
s.id AS shipment_id,
s.bl_no,
c.id AS customer_id,
c.name AS customer_name
FROM shipments s
FULL OUTER JOIN customers c
ON s.customer_id = c.id;
-- 결과:
-- - 정상 매칭: 양쪽 정보
-- - 고객 없는 배송 (임시): customer_id, name = NULL
-- - 배송 없는 고객 (미사용): shipment_id, bl_no = NULL
-- → 양쪽 데이터 누락 X
-- → 정합성 검증 가능
-- ILIC 는 MySQL 이라 우회 (다음 섹션)
FULL OUTER JOIN 의 정의는?
답:
1. 정의:
키워드:
지원:
MySQL:
합집합 시각화:
┌──── employees ────┐ ┌──── departments ────┐
│ │ │ │
│ Alice (101) │──│ HR (101) │
│ Bob (102) │──│ Engineering (102) │
│ │ │ │
│ Charlie (NULL) │ │ Sales (103) │
└────────────────────┘ └───────────────────────┘
↓
FULL OUTER JOIN 결과:
┌──────────────────────────┐
│ Alice + HR │
│ Bob + Engineering │
│ Charlie + NULL │ ← 왼쪽만
│ NULL + Sales │ ← 오른쪽만
└──────────────────────────┘
결과 4가지 케이스:
1. 양쪽 매칭:
- Alice + HR
- Bob + Engineering
2. 왼쪽만 (LEFT 효과):
- Charlie + NULL
3. 오른쪽만 (RIGHT 효과):
- NULL + Sales
4. (양쪽 NULL 은 발생 X)
LEFT + RIGHT 합친 결과:
LEFT JOIN 결과:
- 양쪽 매칭 (Alice, Bob)
- 왼쪽만 (Charlie + NULL)
RIGHT JOIN 결과:
- 양쪽 매칭 (Alice, Bob)
- 오른쪽만 (NULL + Sales)
FULL OUTER = LEFT + RIGHT:
- 양쪽 매칭 (중복 제거)
- 왼쪽만 + 오른쪽만
행 수:
FULL OUTER JOIN 행 수 =
매칭 행 + 왼쪽만 + 오른쪽만
≥ LEFT JOIN 행 수
≥ RIGHT JOIN 행 수
≥ INNER JOIN 행 수
-- 합집합 (ILIC, 가상 PostgreSQL)
-- 시나리오:
-- shipments: 5건 (1건은 customer_id NULL)
-- customers: 10명 (3명은 사용 X)
SELECT
s.bl_no,
c.name
FROM shipments s
FULL OUTER JOIN customers c
ON s.customer_id = c.id;
-- 결과:
-- - 정상 매칭 4건: 배송 + 고객 (4 행)
-- - 고객 없는 배송 1건: bl_no + NULL (1 행)
-- - 사용 안 된 고객 3명: NULL + name (3 행)
-- 총: 8 행
-- INNER = 4 (정상만)
-- LEFT = 5 (배송 모두)
-- RIGHT = 7 (고객 모두)
-- FULL = 8 (양쪽 모두)
합집합 의미와 결과 행은?
답:
1. 벤다이어그램:
4가지 케이스:
= LEFT + RIGHT:
행 수:
두 NULL 케이스:
Charlie (employees):
- dept_id = NULL
- departments 매칭 X
- LEFT 에서 등장
Sales (departments):
- id = 103
- employees 매칭 X
- RIGHT 에서 등장
FULL OUTER:
- 둘 다 등장!
SELECT e.id, e.name, d.id, d.dept_name
FROM employees e
FULL OUTER JOIN departments d
ON e.dept_id = d.id;
결과:
┌──────┬─────────┬──────┬──────────────┐
│ e.id │ e.name │ d.id │ d.dept_name │
├──────┼─────────┼──────┼──────────────┤
│ 1 │ Alice │ 101 │ HR │
│ 2 │ Bob │ 102 │ Engineering │
│ 3 │ Charlie │ NULL │ NULL │ ← LEFT 효과
│ NULL │ NULL │ 103 │ Sales │ ← RIGHT 효과
└──────┴─────────┴──────┴──────────────┘
비교 — INNER JOIN:
결과:
- Alice + HR
- Bob + Engineering
→ 2 행
Charlie, Sales 모두 제외
→ INNER 는 정상 매칭만
→ FULL 은 양쪽 누락 X
식별 활용:
Charlie 케이스 식별:
- WHERE d.id IS NULL
- "부서 없는 직원"
Sales 케이스 식별:
- WHERE e.id IS NULL
- "직원 없는 부서"
둘 다 한 번에:
- WHERE e.id IS NULL OR d.id IS NULL
- "매칭 안 된 모든 데이터"
-- Charlie + Sales 패턴 (ILIC)
-- 시나리오: 정합성 검증
-- shipments 의 customer_id 와 customers 의 id 양방향 확인
-- 모든 매칭 안 된 케이스 (PostgreSQL)
SELECT
s.id AS shipment_id,
s.customer_id AS shipment_customer_id,
c.id AS customer_id,
CASE
WHEN s.id IS NULL THEN '고객 미사용'
WHEN c.id IS NULL THEN '배송 고객 누락'
ELSE '정상'
END AS status
FROM shipments s
FULL OUTER JOIN customers c
ON s.customer_id = c.id
WHERE s.id IS NULL OR c.id IS NULL;
-- 결과:
-- - "고객 미사용" 행들
-- - "배송 고객 누락" 행들
-- → 한 번에 양방향 정합성 확인
-- MySQL 은 우회 (다음 섹션)
Charlie + Sales 모두 등장의 이유는?
답:
1. Charlie:
Sales:
FULL OUTER:
vs INNER:
MySQL 의 현실:
- FULL OUTER JOIN 키워드 지원 X
- SQL 표준이지만 미구현
- 우회 필요
→ ILIC 도 MySQL
-- MySQL 에서 시도
SELECT *
FROM employees e
FULL OUTER JOIN departments d
ON e.dept_id = d.id;
-- 에러:
-- ERROR 1064 (42000): You have an error in your SQL syntax;
-- ... near 'FULL OUTER JOIN'
이유:
- MySQL 의 디자인 선택
- LEFT/RIGHT 만으로 표현 가능
- 구현 복잡도
- 사용 빈도 낮음
→ MariaDB 도 동일
우회 (다음 섹션):
방법 1: LEFT JOIN UNION RIGHT JOIN
방법 2: LEFT JOIN UNION 차집합 LEFT JOIN
ILIC 의 현실 (MySQL)
ILIC = MySQL 8.x
- FULL OUTER JOIN 직접 사용 X
- 우회 필요
하지만 실무에서:
- FULL OUTER JOIN 자주 안 씀
- 보통 LEFT JOIN 으로 충분
- 가끔 정합성 검증에만
→ 우회 방법 알아두기
MySQL 이 FULL OUTER JOIN 지원 X 사실은?
답:
1. 현실:
에러:
이유:
우회:
-- LEFT JOIN UNION RIGHT JOIN
SELECT e.id, e.name, d.id AS dept_id, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id
UNION
SELECT e.id, e.name, d.id AS dept_id, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.id;
UNION 의 역할:
두 SELECT 결과 합침:
- 중복 제거
- 같은 컬럼 구조 필요
여기:
- LEFT 결과 (매칭 + 왼쪽만)
- RIGHT 결과 (매칭 + 오른쪽만)
- UNION → 중복 매칭 제거
- = FULL OUTER 효과
UNION ALL 주의:
UNION:
- 중복 제거 (느림)
- FULL OUTER 와 같음
UNION ALL:
- 중복 유지 (빠름)
- 매칭 행 중복!
- FULL OUTER 와 다름
→ UNION 사용
-- 효율적 우회 (큰 데이터)
-- LEFT + 차집합 LEFT
SELECT e.id, e.name, d.id AS dept_id, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id
UNION ALL
SELECT NULL, NULL, d.id, d.dept_name
FROM departments d
WHERE NOT EXISTS (
SELECT 1 FROM employees e WHERE e.dept_id = d.id
);
-- → 매칭 행 중복 X
-- → UNION ALL 가능 (빠름)
-- ILIC MySQL 우회
-- 모든 배송 + 모든 고객 (FULL OUTER 효과)
SELECT
s.id AS shipment_id,
s.bl_no,
c.id AS customer_id,
c.name AS customer_name
FROM shipments s
LEFT JOIN customers c ON s.customer_id = c.id
UNION
SELECT
s.id,
s.bl_no,
c.id,
c.name
FROM shipments s
RIGHT JOIN customers c ON s.customer_id = c.id;
-- 결과:
-- - 정상 매칭 (중복 제거됨)
-- - 고객 없는 배송
-- - 배송 없는 고객
-- → FULL OUTER 효과
-- 또는 효율적 버전 (큰 데이터)
SELECT s.id, s.bl_no, c.id, c.name
FROM shipments s
LEFT JOIN customers c ON s.customer_id = c.id
UNION ALL
SELECT NULL, NULL, c.id, c.name
FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM shipments s WHERE s.customer_id = c.id
);
LEFT UNION RIGHT 우회 방법은?
답:
1. 방법:
UNION:
UNION ALL 주의:
효율적:
포함 관계:
INNER ⊂ LEFT ⊂ FULL
INNER ⊂ RIGHT ⊂ FULL
LEFT ∪ RIGHT = FULL
LEFT ∩ RIGHT = INNER (행 기준)
→ 4가지 JOIN 의 본질
4가지 JOIN 벤다이어그램:
┌────────────────────────┐
│ FULL │ ← 양쪽 모두
│ ┌────────┐ ┌────────┐ │
│ │ LEFT │ │ RIGHT │ │
│ │ ┌────┼─┼───┐ │ │
│ │ │ INNER (교집합)│ │ │
│ │ └────┼─┼───┘ │ │
│ └────────┘ └────────┘ │
│ (A만) (B만) │
└────────────────────────┘
| JOIN | 결과 행 |
|---|---|
| INNER | 양쪽 매칭 (교집합) |
| LEFT | 매칭 + 왼쪽만 |
| RIGHT | 매칭 + 오른쪽만 |
| FULL | 매칭 + 왼쪽만 + 오른쪽만 |
수학적 표현:
INNER(A, B) = A ∩ B (조건 만족 행)
LEFT(A, B) = A (+ B 매칭)
RIGHT(A, B) = B (+ A 매칭)
FULL(A, B) = A ∪ B (양쪽 모두)
LEFT ∪ RIGHT = FULL
LEFT ∩ RIGHT = INNER
ILIC 의 4가지 JOIN 비교
데이터:
- shipments: 5건 (1건 customer_id NULL)
- customers: 10명 (3명 사용 X)
각 JOIN 결과:
INNER JOIN:
- 정상 매칭만
- 4 행
LEFT JOIN:
- 모든 배송 + 매칭 고객
- 5 행 (1건은 c.name=NULL)
RIGHT JOIN:
- 매칭 + 모든 고객
- 7 행 (3명은 s.id=NULL)
FULL OUTER JOIN:
- 양쪽 모두
- 8 행 (배송 5 + 사용 X 고객 3)
→ INNER (4) < LEFT (5) < RIGHT (7) < FULL (8)
→ 데이터에 따라 다름
4가지 JOIN 포함 관계 (벤다이어그램) 는?
답:
1. 포함:
= 관계:
벤다이어그램:
수학:
행 수 관계 (일반적):
INNER ≤ min(LEFT, RIGHT) ≤ max(LEFT, RIGHT) ≤ FULL
INNER = 매칭 행
LEFT = 매칭 + 왼쪽 매칭 X
RIGHT = 매칭 + 오른쪽 매칭 X
FULL = 매칭 + 왼쪽 X + 오른쪽 X
극단 케이스:
완전 매칭 (모두 짝 있음):
- INNER = LEFT = RIGHT = FULL
- 행 수 같음
완전 불일치 (짝 없음):
- INNER = 0
- LEFT = A 행 수
- RIGHT = B 행 수
- FULL = A + B
→ 데이터 따라
이전 ILIC 예시:
배송 5 / 고객 10:
- INNER: 4 (정상 매칭만)
- LEFT: 5 (배송 전부)
- RIGHT: 7 (매칭 4 + 사용 X 3)
- FULL: 8 (배송 5 + 사용 X 3)
1:N 관계:
customers 1 : N shipments
INNER JOIN: N 행 (배송별로)
LEFT (customers 기준): N + 사용 X 1명당 1 행
→ 1:N 일 때 행 수 ↑
-- ILIC 의 1:N 행 수 (ILIC)
-- 고객 1명에 배송 N개 (1:N)
-- 고객 기준 LEFT JOIN
SELECT c.id, c.name, s.bl_no
FROM customers c
LEFT JOIN shipments s ON s.customer_id = c.id;
-- 결과 행 수:
-- - 배송 있는 고객: 배송 수만큼 행
-- - 배송 없는 고객: 1 행 (bl_no=NULL)
-- 예:
-- 고객 A (배송 3건) → 3 행
-- 고객 B (배송 5건) → 5 행
-- 고객 C (배송 0건) → 1 행 (NULL)
-- 총: 3 + 5 + 1 = 9 행
-- 같은 고객이 여러 번 등장 (배송별)
FULL OUTER JOIN 의 행 수는?
답:
1. 관계:
극단:
예시:
1:N:
자주 안 씀:
실무에서 FULL OUTER:
- 드물게 사용
- 대부분 LEFT 로 충분
- 특수 케이스만
-- 두 시스템 간 데이터 정합성
SELECT
s1.id AS local_id,
s2.id AS remote_id,
CASE
WHEN s1.id IS NULL THEN '로컬 누락'
WHEN s2.id IS NULL THEN '원격 누락'
ELSE '정상'
END AS status
FROM shipments_local s1
FULL OUTER JOIN shipments_remote s2
ON s1.id = s2.id
WHERE s1.id IS NULL OR s2.id IS NULL;
-- → 양쪽 누락 식별
차집합 양방향:
LEFT JOIN + IS NULL:
- 한 방향만
FULL OUTER + IS NULL:
- 양 방향 한 번에
- 누락 / 추가 모두
-- 두 기간 비교
SELECT
COALESCE(a.product_id, b.product_id) AS product_id,
a.sales_2025,
b.sales_2026
FROM sales_2025 a
FULL OUTER JOIN sales_2026 b
ON a.product_id = b.product_id;
-- → 2025/2026 양쪽 모두 상품
-- → 한 해만 팔린 상품도 포함
-- 실무 활용 (ILIC)
-- 시나리오: ILIC 시스템 마이그레이션 정합성
-- 구 시스템과 새 시스템 비교 (MySQL 우회)
SELECT
o.id AS old_id,
n.id AS new_id,
o.bl_no AS old_bl,
n.bl_no AS new_bl
FROM shipments_old o
LEFT JOIN shipments_new n ON o.id = n.id
WHERE n.id IS NULL -- 구에만 있음
UNION
SELECT
o.id,
n.id,
o.bl_no,
n.bl_no
FROM shipments_old o
RIGHT JOIN shipments_new n ON o.id = n.id
WHERE o.id IS NULL; -- 신에만 있음
-- → 양쪽 시스템의 누락 / 추가 식별
-- → 마이그레이션 검증
-- ILIC 의 운영 안 됨:
-- - FULL OUTER 거의 안 씀
-- - 보통 LEFT JOIN 으로 충분
-- - 특수 검증에만
FULL OUTER JOIN 의 실무 활용은?
답:
1. 자주 X:
정합성 검증:
차집합 양방향:
합집합 비교:
| Q | 핵심 답변 |
|---|---|
| FULL OUTER? | 양쪽 모두 |
| 합집합? | A ∪ B |
| Charlie+Sales? | 모두 등장 |
| MySQL? | 지원 X |
| 우회? | LEFT UNION RIGHT |
| 4가지 관계? | INNER⊂LEFT⊂FULL |
| 행 수? | FULL 최대 |
| 1:N? | 행 ↑ |
| 실무? | 드물게 (정합성) |
| LEFT∪RIGHT? | = FULL |
답:
답:
답:
답:
답:
1. FULL OUTER JOIN = 합집합
2. MySQL 의 우회
LEFT JOIN UNION RIGHT JOIN 으로 우회 (중복 제거 위해 UNION ALL 아닌 UNION)3. 4가지 JOIN 의 관계
이번 Unit에서 4가지 JOIN 을 모두 봤다면, 다음은 선택 가이드 (★ 깊이, Phase 1 마지막).
📚 Phase 1 — SQL JOIN
✅ Unit 1.1 JOIN 이 필요한 이유
✅ Unit 1.2 INNER JOIN
✅ Unit 1.3 LEFT/RIGHT JOIN
✅ Unit 1.4 FULL OUTER JOIN ← 여기
⏭ Unit 1.5 JOIN 선택 가이드 ★깊이 — Phase 1 완주
🗂️ Part A — 데이터 모델링과 ORM
📚 Phase 1 (4/5)
총: 4/24 Unit
F-LAB JAVA · 7주차 · Phase 1 · Unit 1.4 · 끝