[Database] MySQL 실습(3) - JOIN + 연습문제

우유·2026년 2월 2일

[Cloud] Database

목록 보기
6/28

3. 조인


1) JOIN이 필요한 이유

현재 테이블 구조는 의도적으로 정규화되어 있다. 즉, 데이터가 중복되지 않도록 테이블이 나뉘어져 있음.

테이블역할
customers고객 정보
products상품 정보
orders주문(누가 언제 주문했는지)
order_items주문에 포함된 상품
payments결제 정보

예를 들어:

“누가 무엇을 얼마에 주문했는가?”

이 질문은 단일 테이블로는 절대 답할 수 없다.

👉 그래서 JOIN이 필요하다.


2) JOIN 기본 문법

SELECT 컬럼들
FROM 테이블A a
JOIN 테이블B b ON a.공통컬럼 = b.공통컬럼;
  • JOIN의 기준은 항상 관계(FK → PK)
  • ON 조건이 JOIN의 핵심

3) INNER JOIN 실습

3-1. 주문 + 고객 정보 JOIN

“주문 목록에 고객 이름을 같이 보고 싶다”

SELECT
  o.order_id,
  o.ordered_at,
  o.status,
  o.total_amount,
  c.name AS customer_name
FROM orders o
(INNER )JOIN customers c
  ON o.customer_id = c.customer_id;

Oracle 방식

SELECT
  o.order_id,
  o.ordered_at,
  o.status,
  o.total_amount,
  c.name AS customer_name
FROM orders o, customers c
WHERE o.customer_id = c.customer_id;

핵심

  • orders.customer_id → customers.customer_id (FK → PK)
  • INNER JOIN: 양쪽에 모두 존재하는 데이터만

실습 문제

  1. orders + customers를 JOIN하여 order_id, 고객명, 주문상태, 주문일자를 조회하라.
select
	o.order_id,
    c.name as customer_name,
    o.status,
    o.ordered_at
from orders o
join customers c
  on o.customer_id = c.customer_id

3-2. 주문 + 주문상품 JOIN

“주문 하나에 어떤 상품들이 들어 있는가?”

SELECT
  o.order_id,
  oi.product_id,
  oi.quantity,
  oi.unit_price,
  oi.line_amount
FROM orders o
JOIN order_items oi
  ON o.order_id = oi.order_id;

실습 문제

  1. order_items + products를 JOIN하여 상품명, 수량, 단가, 금액을 조회하라.
SELECT
  p.name,
  oi.quantity,
  oi.unit_price,
  oi.line_amount
FROM products p
JOIN order_items oi
  ON p.product_id = oi.product_id;

3-3. 주문 + 주문상품 + 상품 (3테이블 JOIN)

SELECT
  o.order_id,
  p.name AS product_name,
  oi.quantity,
  oi.unit_price,
  oi.line_amount
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p     ON oi.product_id = p.product_id
ORDER BY o.order_id;

Oracle 방식

SELECT
  o.order_id,
  p.name AS product_name,
  oi.quantity,
  oi.unit_price,
  oi.line_amount
FROM orders o, order_items oi, products p
WHERE o.order_id = oi.order_id
  AND oi.product_id = p.product_id
ORDER BY o.order_id;

강의 포인트

  • JOIN은 여러 개를 연속으로 연결 가능
  • 기준 테이블은 보통 업무 질문의 중심

실습 문제

  1. 주문별로 상품명 + 수량 + 금액을 모두 조회하라.
SELECT
  o.order_id,
  p.name AS product_name,
  oi.quantity,
  oi.line_amount
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p     ON oi.product_id = p.product_id
group by o.order_id, product_name
ORDER BY o.order_id;

4) JOIN + WHERE (조건 결합)

4-1. 특정 고객의 주문 내역

SELECT
  o.order_id,
  o.ordered_at,
  o.total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE c.name = 'Kim Mina'
ORDER BY o.ordered_at DESC;

실습 문제

  1. 고객 이름이 Lee Jisu인 고객의 주문 내역을 조회하라.
SELECT
  c.name
  o.order_id,
  o.ordered_at,
  o.total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE c.name = 'Lee Jisu'
ORDER BY o.ordered_at DESC;

4-2. 취소되지 않은 주문만 조회

SELECT
  o.order_id,
  c.name,
  o.status,
  o.total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.status <> 'CANCELLED';

5) LEFT (OUTER) JOIN 실습 (중요)

5-1. 주문 + 결제 (결제 없는 주문 포함)

“결제가 아직 안 된 주문도 보고 싶다”

SELECT
  o.order_id,
  o.status AS order_status,
  p.method,
  p.status AS payment_status
FROM orders o
LEFT JOIN payments p
  ON o.order_id = p.order_id
ORDER BY o.order_id;

핵심

  • LEFT JOIN: 왼쪽 테이블(위 sql문에서는 orders가 왼쪽테이블)은 무조건 유지
  • 결제가 없는 주문 → payment 컬럼은 NULL

5-2. “결제되지 않은 주문 찾기”

SELECT
  o.order_id,
  o.status,
  o.total_amount
FROM orders o
LEFT JOIN payments p ON o.order_id = p.order_id
WHERE p.order_id IS NULL;

실습 문제

  1. 주문이력이 없는 이용자 조회하라.
select c.*, order_id from customers c
left join orders o on c.customer_id = o.customer_id
where order_id is null;

6) JOIN + GROUP BY

6-1. 고객별 주문 건수

SELECT
  c.customer_id,
  c.name,
  COUNT(o.order_id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name
ORDER BY order_count DESC;

  • 집계 시 GROUP BY는 비집계 컬럼 전부 포함
  • LEFT JOIN → 주문 없는 고객도 포함 가능

실습 문제

  1. 고객별 총 주문 금액 합계를 구하라.

6-2. 상품별 판매 수량

SELECT
  p.product_id,
  p.name,
  SUM(oi.quantity) AS total_qty
FROM products p
JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY p.product_id, p.name
ORDER BY total_qty DESC;

실습 문제

  1. 상품별 총 매출 금액(quantity * unit_price)을 구하라.

7) 종합

7-1. 최근 7일 주문 상세 리포트

SELECT
  o.order_id,
  c.name AS customer_name,
  o.ordered_at,
  p.name AS product_name,
  oi.quantity,
  oi.line_amount
FROM orders o
JOIN customers c   ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p     ON oi.product_id = p.product_id
WHERE o.ordered_at >= NOW() - INTERVAL 7 DAY
ORDER BY o.ordered_at DESC;

7-2. 결제 수단별 매출

SELECT
  p.method,
  SUM(p.paid_amount) AS total_sales
FROM payments p
GROUP BY p.method
ORDER BY total_sales DESC;

8) 요약

  • JOIN은 테이블을 합치는 기술이 아니라 관계를 해석하는 기술
  • INNER JOIN → “둘 다 있어야 의미 있음”
  • LEFT JOIN → “기준 테이블은 무조건 살린다”
  • 실무 리포트 = JOIN + WHERE + GROUP BY

연습문제

조인 (10문제)

Q1. 주문 1건(order_id=1)의 주문자 이름과 주문일시를 조회하시오.
(orders + customers)

SELECT o.order_id, c.name AS customer_name, o.ordered_at
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
WHERE o.order_id = 1;

Q2. 결제가 존재하는 주문에 대해 주문번호, 고객명, 결제수단, 결제금액을 조회하시오.
(orders + customers + payments)

SELECT o.order_id, c.name AS customer_name, p.method, p.paid_amount
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
JOIN payments p ON p.order_id = o.order_id;

Q3.(서브쿼리 문제) 각 주문의 주문금액(orders.total_amount)과 “실제 주문상세 합계(SUM(order_items.line_amount))”를 함께 보여 차이가 있는 주문만 조회하시오.
(orders + order_items)

JOIN 절 서브쿼리

select o.order_id, o.total_amount, x.sum_line_amount
from orders o
join (
	select order_id, sum(line_amount) as sum_line_amount
    from order_items
    group by order_id ) x on x.order_id = o.order_id
where o.tatal_amount != x.sum_line_amount;

SELECT 절 서브쿼리 (스칼라 서브쿼리)

select o.order_id, o.total_amount, (
	select sum(oi.line_amount)
    from order_items oi
    where o.order_id = oi.order_id
) as total_line_amount
from orders o
where o.total_amount=total_line_amount;

원했던 모양은 위와 같지만 WHERE절에서는 별칭을 사용할 수 없다.
따라서, 아래와 같이 작성되어야 하기때문에 JOIN절 서브쿼리를 쓰는것이 더 나아보인다.

select o.order_id, o.total_amount, (
	select sum(oi.line_amount)
    from order_items oi
    where o.order_id = oi.order_id
) as total_line_amount
from orders o
where o.total_amount=(
	select sum(oi.line_amount)
    from order_items oi
    where o.order_id = oi.order_id
);

FROM 절 한 번 감싸서 alias 사용 (깔끔한 패턴)

select *
from (
    select
        o.order_id,
        o.total_amount,
        (
            select sum(oi.line_amount)
            from order_items oi
            where oi.order_id = o.order_id
        ) as total_line_amount
    from orders o
) t
where t.total_amount != t.total_line_amount;

Q4. 주문상세 기준으로, 주문번호별 아이템 개수(라인 수)를 조회하시오.
(출력: order_id, item_lines)

SELECT oi.order_id, COUNT(*) AS item_lines
FROM order_items oi
GROUP BY oi.order_id;

Q5. 주문상세 기준으로 상품별 총 판매수량(quantity 합)을 조회하고, 판매수량 TOP 5를 출력하시오.
(order_items + products)

SELECT p.product_id, p.name, SUM(oi.quantity) AS total_qty
FROM order_items oi
JOIN products p ON p.product_id = oi.product_id
GROUP BY p.product_id, p.name
ORDER BY total_qty DESC
LIMIT 5;

Q6. 고객별 총 주문금액(orders.total_amount 합)을 조회하고, 총액 TOP 5를 출력하시오.
(orders + customers)

SELECT c.customer_id, c.name, SUM(o.total_amount) AS total_spend
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name
ORDER BY total_spend DESC
LIMIT 5;

Q7. 아직 결제가 없는 주문(미결제)을 조회하시오.
(orders LEFT JOIN payments)

SELECT o.order_id, o.customer_id, o.status, o.ordered_at
FROM orders o
LEFT JOIN payments p ON p.order_id = o.order_id
WHERE p.order_id IS NULL;

Q8. 주문상세와 상품을 조인해서, 주문 10번의 상품명/수량/단가/라인금액을 출력하시오.
(order_items + products)

SELECT oi.order_id, p.name AS product_name, oi.quantity, oi.unit_price, oi.line_amount
FROM order_items oi
JOIN products p ON p.product_id = oi.product_id
WHERE oi.order_id = 10
ORDER BY oi.order_item_id;

Q9. 고객별 주문 건수와 최근 주문일(ordered_at MAX)을 함께 조회하시오.
(orders + customers)

SELECT c.customer_id, c.name,
       COUNT(o.order_id) AS order_cnt,
       MAX(o.ordered_at) AS last_ordered_at
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
GROUP BY c.customer_id, c.name;

주문이 없는 고객까지 전부 출력 (LEFT JOIN)

Q10. SHIPPED 상태 주문에 대해 주문번호, 고객명, 주문일시, 주문금액을 조회하시오.
(orders + customers)

SELECT o.order_id, c.name AS customer_name, o.ordered_at, o.total_amount
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
WHERE o.status = 'SHIPPED'
ORDER BY o.ordered_at DESC;
profile
Front-end Developer, Cloud Engineer

0개의 댓글