AI 시대 DB 모델링 & SQL 완벽 마스터3-7 연습 문제 ⭐
테크타니 LEARN — 섹션 03 · 연습 문제

3-7 연습 문제 — JOIN 뻥튀기 함정,
AI 를 의심하는 능력을 직접 기른다

이번 연습의 목표는 단 하나 — AI 를 의심하는 능력을 직접 기르는 것입니다. 눈으로 읽은 SQL 은 내일이면 사라지지만, 직접 짜고 직접 검산해본 SQL 만 머리에 남습니다. 풀어본 뒤에는 본인 결과를 ChatGPT·Claude 에 던지고 "이 결과가 정말 맞는지 검증해줘" 라고 물어보세요.

문제3문제 (도전 1 포함)권장 소요15~20분사용 테이블members · orders · order_items · products준비물3-7 수강 완료

풀기 전에 — 무기 점검

3-1 ~ 3-5

완료 ✓

3-6 JOIN 핵심

완료 ✓

3-7 JOIN 뻥튀기 함정 ⭐

완료 ✓

3-7 연습 문제

지금 여기

3-8 CASE / 서브쿼리

다음 마일스톤

이번 강의에서 배운 것 — 이 문제들에 그대로 씁니다
1:N 의 N 쪽으로 JOIN 할수록 행이 곱해진다 (뻥튀기)
JOIN 직후 COUNT(*) 검산 — 가장 자식 테이블 행 수와 비교
COUNT(DISTINCT ...) 로 중복 제거
필요한 만큼만 JOIN — 가장 깔끔한 해법
AI 검증 체크리스트 3가지
AI 과외 선생님 활용법

2-4에서 만든 AI 과외 선생님 세션을 그대로 쓰세요. 막히면 정답이 아니라 힌트만 요청하고 — “정답 말고 힌트만 줘” — 다 푼 뒤에는 본인 SQL 과 결과를 던지면서 “이 결과가 정말 맞는지 검증해줘” 라고 물어보세요. 이번 강의의 주제가 바로 그 검증 능력입니다.

문제 1. 4-테이블 JOIN 행 수 검산

members → orders → order_items → products 를 모두 JOIN 했을 때 — 총 행 수는 몇 행 일까요? 그리고 그 행 수가 의미하는 것은?

기대 결과 — 1 row
COUNT(*)
64
1 row fetched — 뻥튀기 없이 정상 (order_items 행 수와 동일)
힌트가장 자식 테이블의 행 수와 같다면 정상.
정답 보기
SELECT
  COUNT(*)
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;
-- → 64 (= order_items 행 수)

의미: members 가 부모, order_items 가 가장 자식. 4-테이블 JOIN 의 행 수는 가장 자식인 order_items 의 행 수와 같다 — 뻥튀기 없이 정상.

문제 2. 회원별 총 매출 (정확하게)

각 회원이 지출한 총 매출액 (= price × quantity 의 합). 매출 많은 순으로.

기대 결과 — 16 rows
member_name총매출
조성민600000
김철수498000
16 rows fetched — 주문한 회원 16명만 (INNER JOIN)
정답 보기
SELECT
  m.member_name,
  SUM(p.price * oi.quantity) AS 총매출
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
GROUP BY
  m.member_id,
  m.member_name
ORDER BY
  총매출 DESC;

문제 3 (도전). AI 의심 시뮬레이션 🔍

다음 SQL 은 AI 가 “회원별 주문 수를 뽑아줘” 라는 요청에 만든 답 입니다. DBeaver 에서 직접 실행해보세요.

-- ❓ AI 가 만든 SQL — 이거 맞을까?
SELECT
  m.member_name,
  COUNT(*) AS 주문수
FROM
  members m
JOIN orders o
  ON m.member_id = o.member_id
JOIN order_items oi
  ON o.order_id = oi.order_id
GROUP BY
  m.member_id,
  m.member_name
ORDER BY
  주문수 DESC;

질문 4가지:

  1. 결과에서 김철수의 주문 수 가 몇으로 나오나요?
  2. 그게 맞을까요? (정답: 김철수는 실제 6 건 주문)
  3. 틀렸다면 어디가 잘못된 건가요?
  4. 올바르게 고친 SQL 을 짜보세요.
해설 보기
AI 의 SQL 실행 결과 — 김철수만 보면
member_name주문수
김철수10
김철수 주문수 10 — 실제 주문은 6 건! (order_items 까지 JOIN 해서 뻥튀기)

1. 결과: 김철수가 10 으로 나옴 — 부풀려진 숫자. 2. 틀렸음. 실제 김철수의 주문은 6 건. 3. 원인: order_items 까지 JOIN 하면서 한 주문이 그 주문상세 개수만큼 행으로 뻥튀기. 그 부풀린 행 수를 COUNT(*) 가 그대로 세버림. 4. 해결책 (2가지 중 택1):

-- (A) order_items 까지 갈 필요 자체가 없음 — 가장 깔끔
SELECT
  m.member_name,
  COUNT(o.order_id) AS 주문수
FROM
  members m
LEFT JOIN orders o
  ON m.member_id = o.member_id
GROUP BY
  m.member_id,
  m.member_name
ORDER BY
  주문수 DESC;
 
-- (B) COUNT(DISTINCT) 로 중복 제거
SELECT
  m.member_name,
  COUNT(DISTINCT o.order_id) AS 주문수
FROM
  members m
JOIN orders o
  ON m.member_id = o.member_id
JOIN order_items oi
  ON o.order_id = oi.order_id
GROUP BY
  m.member_id,
  m.member_name
ORDER BY
  주문수 DESC;
✕ COUNT(*)

부풀린 행을 그대로 센다

한 주문이 주문상세 개수만큼 행으로 늘어난 상태 — 김철수의 6 건 주문이 10 행이 되고, COUNT(*) 는 그 10 을 그대로 셉니다.

✓ COUNT(DISTINCT o.order_id)

중복 order_id 는 한 번만

행이 아무리 뻥튀기돼도 order_id 는 6 종류뿐 — 6 으로 정상화. 애초에 order_items 를 JOIN 하지 않는 (A) 가 가장 깔끔합니다.

핵심 교훈

AI 는 SQL 문법은 잘 짭니다. 근데 “이 JOIN 이 의미가 맞는지” 는 못 봅니다. JOIN 직후 COUNT(*) 검산 → 가장 자식 테이블 행 수와 일치하는지 확인 — 이게 평생의 습관입니다.

세 문제를 모두 직접 검산까지 해봤다면 — 실무에서 가장 비싼 SQL 사고를 미리 겪고 백신까지 맞은 셈입니다. 이제 섹션 3 의 마지막 퍼즐 조각, 조건 분기와 쿼리 속의 쿼리로 갑니다.

NEXT LESSON3-8 CASE / 서브쿼리

조건에 따라 값을 바꾸는 CASE, 쿼리 안에 쿼리를 넣는 서브쿼리 — 섹션 3 의 마지막 무기입니다.

GO ▸ 다음 강의 시작하기