AI 시대 DB 모델링 & SQL 완벽 마스터3-6 JOIN 핵심
테크타니 LEARN — 섹션 03 · SQL 핵심 문법 — 한방 압축

쪼개진 테이블을 다시 하나로,
JOIN — 두 개만 제대로

여기가 진짜 SQL의 시작입니다. 실무 데이터는 회원 정보는 회원 테이블에, 주문 내역은 주문 테이블에 — 여러 테이블에 쪼개져 있고, 이걸 옆으로 합치는 명령이 JOIN입니다. 종류는 4~5개지만 현실에서 쓰는 건 거의 INNER JOIN과 LEFT JOIN 둘뿐 — 이 강의에선 두 개만 제대로 익힙니다.

난이도보통소요15분준비물shop DB (2-3 적재 완료)오늘의 무기INNER JOIN · LEFT JOIN

이번 섹션의 여정

3-1 ~ 3-3

완료 ✓

3-4 ~ 3-5

완료 ✓

3-6 JOIN 핵심

지금 여기

3-7 JOIN 뻥튀기 함정 ⭐

AI가 가장 자주 틀리는 패턴

3-8 CASE / 서브쿼리

섹션 3 마무리

섹션 4 — DB 모델링

강의 메인

데이터는 쪼개져 있습니다 — 그래서 JOIN

지금까지 다섯 강의 내내 한 테이블만 다뤘죠. 근데 실무 데이터는 그렇게 생기지 않았습니다. 회원 정보는 members에, 주문 내역은 orders에 — 여러 테이블에 쪼개져 있어요. “회원 이름과 그 회원의 주문일을 한 표로 보고 싶다” 는 순간, 두 테이블을 합쳐야 합니다. 그게 JOIN“두 테이블을 어떤 컬럼 기준으로 옆으로 붙인다” 는 명령입니다.

핵심은 “어떤 컬럼 기준으로?” 라는 질문이에요. 답은 우리가 2-3에서 본 그 구조 — 부모-자식 관계입니다.

shop DB — members와 orders의 관계
members (부모) orders (자식)
┌──────────────────┐ ┌──────────────────┐
│ member_id (PK) │◄── 참조 ──────│ member_id (FK) │
│ member_name │ │ order_id (PK) │
│ gender · age │ │ ordered_at │
└──────────────────┘ └──────────────────┘
 
회원 1명 → 주문 여러 건 (1:N) — members가 부모, orders가 자식

membersmember_id가 기본키(PK), ordersmember_id가 그걸 가리키는 외래키(FK)죠. 그래서 JOIN의 ON 조건은 거의 항상 “부모ID = 자식ID” 입니다.

... ON m.member_id = o.member_id
JOIN의 90%

ON 부모ID = 자식ID. 이 한 줄이 JOIN의 90%입니다. 어떤 JOIN을 만나도 — 먼저 부모와 자식이 누군지부터 찾으세요.

이 기준이 왜 중요하냐면 —

✕ 기준 없이 붙이면

800행의 무의미한 조합

ON 없이 붙이면 회원 20명 × 주문 40건 — 모든 조합 800행이 쏟아집니다. 김철수 옆에 남의 주문이 붙어 있는, 아무 의미 없는 표.

✓ 부모ID = 자식ID로 붙이면

정확히 40행 — 주문과 주인이 짝지어짐

각 주문이 자기 주인 회원하고만 연결됩니다. ON 조건이 바로 이 “붙이는 기준”을 정하는 자리예요.

INNER JOIN — 양쪽 다 매칭되는 행만

첫 번째 무기부터 실행해 볼게요. 회원 이름 + 그 회원의 주문ID + 주문일을 한 줄로 묶습니다.

SELECT
  m.member_name,
  o.order_id,
  o.ordered_at
FROM
  members m
INNER JOIN orders o
  ON m.member_id = o.member_id;
DBeaver — SQL Editor (Ctrl + Enter)
SELECT m.member_name, o.order_id, o.ordered_at FROM members m INNER JOIN orders o ON m.member_id = o.member_id;
member_nameorder_idordered_at
김철수12025-02-01
김철수62025-04-10
이영희22025-02-15
박민수32025-03-01
주문한 16명만 등장 — 주문 없는 4명은 결과에서 사라짐
40 rows fetched · 0.004s

두 테이블에 흩어져 있던 정보가 한 줄로 붙었습니다. 여기서 문법 하나 짚고 갈게요.

별칭(Alias) — JOIN에선 거의 필수

members m“members 테이블을 m이라고 부르겠다” 는 선언이에요. 별칭 없이는 members.member_name, orders.order_id를 매번 길게 써야 합니다. 관습은 테이블 첫 글자m(members), o(orders), p(products).

그리고 INNER JOIN의 의미. INNER JOIN은 양쪽 다 매칭되는 행만 남깁니다. shop DB에서는 20명 중 16명이 주문했고 4명은 한 번도 안 했죠 — 그래서 결과에는 주문한 16명만 나옵니다. 반대로 만약 부모 없는 자식(FK가 깨진 상태의 주문)이 있어도 똑같이 안 나와요.

INNER는 생략 가능

JOIN만 써도 INNER JOIN입니다. 실무 코드에선 생략형을 더 자주 봐요.

-- 두 줄은 완전히 같은 의미
... FROM members m JOIN orders o ON m.member_id = o.member_id
... FROM members m INNER JOIN orders o ON m.member_id = o.member_id

LEFT JOIN — 왼쪽은 모두 보존

이번엔 요구가 바뀝니다 — “주문 안 한 회원도 보고 싶다”. INNER JOIN으로는 불가능하죠, 4명이 사라지니까. 이럴 때 두 번째 무기.

SELECT
  m.member_name,
  o.order_id
FROM
  members m
LEFT JOIN orders o
  ON m.member_id = o.member_id;
DBeaver — SQL Editor (Ctrl + Enter)
SELECT m.member_name, o.order_id FROM members m LEFT JOIN orders o ON m.member_id = o.member_id;
member_nameorder_id
김철수1
김철수6
황태웅NULL
안소영NULL
류재민NULL
백서윤NULL
44 rows fetched — 주문 없는 4명도 NULL로 살아 있음
LEFT JOIN의 의미

왼쪽 테이블(members)은 모두 보존, 오른쪽(orders)은 매칭되는 것만. 주문 안 한 회원도 결과에 나오고 — 매칭이 안 된 오른쪽 칸은 NULL로 채워집니다.

이 NULL이 다음 강의 함정의 출발점

LEFT JOIN이 만들어내는 NULL은 3-7에서 다룰 함정의 씨앗입니다. 지금은 딱 하나만 기억하세요 — “매칭 안 되면 NULL”.

LEFT JOIN + IS NULL — 자식 없는 부모 찾기

방금 결과를 자세히 보면 재밌는 게 보여요. 주문 안 한 회원만 order_id가 NULL이죠? 그럼 NULL인 행만 골라내면 — “주문이 한 번도 없는 회원” 이 나옵니다.

-- 주문이 한 번도 없는 회원
SELECT
  m.member_name
FROM
  members m
LEFT JOIN orders o
  ON m.member_id = o.member_id
WHERE
  o.order_id IS NULL;
DBeaver — SQL Editor (Ctrl + Enter)
SELECT m.member_name FROM members m LEFT JOIN orders o ON m.member_id = o.member_id WHERE o.order_id IS NULL;
member_name
황태웅
안소영
류재민
백서윤
4 rows fetched — 휴면 회원 명단 완성
외워둘 패턴 ⭐

LEFT JOIN + IS NULL = “자식 없는 부모 찾기”. “한 번도 주문 안 한 회원”, “리뷰 없는 상품”, “댓글 없는 게시글” — 전부 이 한 패턴입니다. 실무에서 정말 자주 쓰여요.

INNER vs LEFT — 언제 어떤 걸 쓰나

오늘 배운 걸 한 표로 정리하면 이렇게 됩니다.

결과에 들어가는 행언제 쓰나
INNER JOIN양쪽 다 매칭되는 행만”주문 한 회원만 보고 싶다”
LEFT JOIN왼쪽은 모두 보존 + 매칭되면 오른쪽도 (없으면 NULL)“전체 회원 + 그 회원의 주문”
LEFT JOIN + IS NULL매칭 안 된 왼쪽만”주문 안 한 회원만 보고 싶다”
세 패턴이면 충분

요구사항이 들어오면 먼저 물으세요 — “매칭 안 되는 쪽을 버릴 건가, 살릴 건가?” 버리면 INNER, 살리면 LEFT, 매칭 안 된 것만 보고 싶으면 LEFT + IS NULL. 이 세 패턴이 머릿속에 있으면 현실 SQL의 90%는 다 짤 수 있습니다.

3개 이상 JOIN — 체인으로 잇기

“JOIN은 두 테이블까지만 되나요?” — 아니요. 체인처럼 계속 이어 붙일 수 있습니다. 회원 → 주문 → 주문상세 → 상품, 4개 테이블을 한 번에 묶어볼게요.

-- 회원 → 주문 → 주문상세 → 상품
SELECT
  m.member_name,
  o.ordered_at,
  p.product_name,
  oi.quantity
FROM
  members m
JOIN orders o
  ON m.member_id = o.member_id
JOIN order_items oi
  ON o.order_id = oi.order_id
JOIN products p
  ON oi.product_id = p.product_id
ORDER BY
  m.member_name,
  o.ordered_at;
JOIN 체인 — 부모-자식 흐름만 따라가면 됩니다
members ──member_id──▶ orders ──order_id──▶ order_items
(부모) (자식이자 부모) (자식)
 
products ──product_id──▶ order_items
(부모) (자식)
JOIN 체인 결과 — 누가 · 언제 · 뭘 · 몇 개
member_nameordered_atproduct_namequantity
강서연2025-04-20라운드 티셔츠2
강서연2025-04-20니트1
강서연2025-09-05SQL 입문서1
강서연2026-01-05카디건1
64 rows fetched — 누가 · 언제 · 뭘 · 몇 개 샀는지 한 표로

ON 조건 세 개를 보세요 — 전부 부모ID = 자식ID입니다. 회원이 부모, 주문이 자식. 주문이 부모, 주문상세가 자식. 상품이 부모, 주문상세가 자식. 관계의 흐름만 따라가면 JOIN이 몇 개든 그냥 풀려요.

모델링 알면 JOIN은 자동

핵심 원칙 — 부모-자식 관계 흐름대로 JOIN한다. 테이블 관계도가 머릿속에 그려지면 ON 조건은 고민할 게 없습니다. 이게 바로 “DB 모델링이 SQL의 출발점” 이라는 이 강의 메시지의 실증이고 — 섹션 4에서 그 모델링을 제대로 배웁니다.

자주 막히는 지점

① 결과가 갑자기 수백 행으로 폭발해요

ON 조건을 빼먹으면 모든 조합(회원 20 × 주문 40 = 800행)이 만들어집니다 — 에러가 아니라 조용히 잘못된 결과가 나오는 게 더 무서운 점이에요.

FIX ▸JOIN 뒤에 ON 부모ID = 자식ID가 있는지 확인하세요. MySQL은 ON 없이도 에러 없이 그냥 돌아갑니다.
② Column 'member_id' is ambiguous 에러가 떠요

member_id가 members와 orders 양쪽에 다 있어서, “어느 쪽 member_id 말하는 거야?” 라고 MySQL이 묻는 겁니다. JOIN에서 별칭이 거의 필수인 이유.

FIX ▸컬럼 앞에 별칭으로 소속을 명시 — m.member_id처럼.
③ LEFT JOIN을 했는데 주문 안 한 회원이 안 나와요

WHERE에 오른쪽 테이블 컬럼 조건(예: o.ordered_at 비교)을 걸면 NULL 행이 전부 걸러져서 INNER JOIN과 똑같아집니다. LEFT JOIN의 “왼쪽 보존”이 무너지는 대표 실수예요.

FIX ▸오른쪽 테이블 조건은 WHERE가 아니라 ON 쪽에 — 단, IS NULL 패턴만은 WHERE가 맞습니다.

INNER와 LEFT, 그리고 IS NULL 패턴까지 — 오늘 배운 세 가지가 손에 익도록 연습 문제부터 풀고 가세요. 다음 강의는 이 JOIN이 데이터를 조용히 뻥튀기하는 함정 — 위 4테이블 쿼리에 GROUP BY를 그대로 얹었다가 회원별 주문 수가 이상하게 불어나는, AI가 가장 자주 틀리는 패턴입니다.

NEXT LESSON3-6 연습 문제

INNER · LEFT · IS NULL — 세 패턴을 shop DB에서 직접 손으로 확인합니다.

GO ▸ 연습 문제 풀기