3-7 연습 문제 — JOIN 뻥튀기 함정,
AI 를 의심하는 능력을 직접 기른다
이번 연습의 목표는 단 하나 — AI 를 의심하는 능력을 직접 기르는 것입니다. 눈으로 읽은 SQL 은 내일이면 사라지지만, 직접 짜고 직접 검산해본 SQL 만 머리에 남습니다. 풀어본 뒤에는 본인 결과를 ChatGPT·Claude 에 던지고 "이 결과가 정말 맞는지 검증해줘" 라고 물어보세요.
풀기 전에 — 무기 점검
완료 ✓
완료 ✓
완료 ✓
지금 여기
다음 마일스톤
2-4에서 만든 AI 과외 선생님 세션을 그대로 쓰세요. 막히면 정답이 아니라 힌트만 요청하고 — “정답 말고 힌트만 줘” — 다 푼 뒤에는 본인 SQL 과 결과를 던지면서 “이 결과가 정말 맞는지 검증해줘” 라고 물어보세요. 이번 강의의 주제가 바로 그 검증 능력입니다.
문제 1. 4-테이블 JOIN 행 수 검산
members → orders → order_items → products 를 모두 JOIN 했을 때 — 총 행 수는 몇 행 일까요? 그리고 그 행 수가 의미하는 것은?
| COUNT(*) |
|---|
| 64 |
정답 보기
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 의 합). 매출 많은 순으로.
| member_name | 총매출 |
|---|---|
| 조성민 | 600000 |
| 김철수 | 498000 |
| ⋮ | |
정답 보기
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가지:
- 결과에서 김철수의 주문 수 가 몇으로 나오나요?
- 그게 맞을까요? (정답: 김철수는 실제 6 건 주문)
- 틀렸다면 어디가 잘못된 건가요?
- 올바르게 고친 SQL 을 짜보세요.
해설 보기
| member_name | 주문수 |
|---|---|
| 김철수 | 10 |
| ⋮ | |
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;부풀린 행을 그대로 센다
한 주문이 주문상세 개수만큼 행으로 늘어난 상태 — 김철수의 6 건 주문이 10 행이 되고, COUNT(*) 는 그 10 을 그대로 셉니다.
중복 order_id 는 한 번만
행이 아무리 뻥튀기돼도 order_id 는 6 종류뿐 — 6 으로 정상화. 애초에 order_items 를 JOIN 하지 않는 (A) 가 가장 깔끔합니다.
AI 는 SQL 문법은 잘 짭니다. 근데 “이 JOIN 이 의미가 맞는지” 는 못 봅니다.
JOIN 직후 COUNT(*) 검산 → 가장 자식 테이블 행 수와 일치하는지 확인 —
이게 평생의 습관입니다.
세 문제를 모두 직접 검산까지 해봤다면 — 실무에서 가장 비싼 SQL 사고를 미리 겪고 백신까지 맞은 셈입니다. 이제 섹션 3 의 마지막 퍼즐 조각, 조건 분기와 쿼리 속의 쿼리로 갑니다.