4 분 소요

프로그래머스 사이트 링크

📌 문제 설명

다음은 어느 한 서점에서 판매 중인 도서들의 도서 정보(BOOK), 저자 정보(AUTHOR), 판매 정보(BOOK_SALES) 테이블입니다.

BOOK 테이블

Column name Type Nullable Description
BOOK_ID INTEGER FALSE 도서 ID
CATEGORY VARCHAR(N) FALSE 카테고리 (경제, 인문, 소설, 생활, 기술)
AUTHOR_ID INTEGER FALSE 저자 ID
PRICE INTEGER FALSE 판매가 (원)
PUBLISHED_DATE DATE FALSE 출판일

AUTHOR 테이블

Column name Type Nullable Description
AUTHOR_ID INTEGER FALSE 저자 ID
AUTHOR_NAME VARCHAR(N) FALSE 저자명

BOOK_SALES 테이블

Column name Type Nullable Description
BOOK_ID INTEGER FALSE 도서 ID
SALES_DATE DATE FALSE 판매일
SALES INTEGER FALSE 판매량


📌 문제

2022년 1월의 도서 판매 데이터를 기준으로 저자 별, 카테고리 별 매출액(TOTAL_SALES = 판매량 * 판매가)을 구하여, 저자 ID(AUTHOR_ID), 저자명(AUTHOR_NAME), 카테고리(CATEGORY), 매출액(TOTAL_SALES) 리스트를 출력하는 SQL문을 작성해주세요.

결과는 저자 ID를 기준으로 오름차순, 저자 ID가 같다면 카테고리를 기준으로 내림차순 정렬해주세요.


📌 예시

BOOK 테이블, AUTHOR 테이블, BOOK_SALES 테이블이 다음과 같을 때

BOOK

BOOK_ID CATEGORY AUTHOR_ID PRICE PUBLISHED_DATE
1 인문 1 10000 2020-01-01
2 경제 1 9000 2021-02-05
3 경제 2 9000 2021-03-11

AUTHOR

AUTHOR_ID AUTHOR_NAME
1 홍길동
2 김영호

BOOK_SALES

BOOK_ID SALES_DATE SALES
1 2022-01-01 2
2 2022-01-02 3
1 2022-01-05 1
2 2022-01-20 5
2 2022-01-21 6
3 2022-01-22 2
2 2022-02-11 3

2022년 1월 도서별 총 매출액은 도서 ID 1이 3권 × 10,000원 = 30,000원, 도서 ID 2가 14권 × 9,000원 = 126,000원, 도서 ID 3이 2권 × 9,000원 = 18,000원입니다.

SQL을 실행하면 다음과 같이 출력되어야 합니다.

AUTHOR_ID AUTHOR_NAME CATEGORY TOTAL_SALES
1 홍길동 인문 30000
1 홍길동 경제 126000
2 김영호 경제 18000

📌 핵심 포인트

이 문제는 3개 테이블을 연결해야 합니다. 각 테이블의 역할을 먼저 파악하는 것이 중요합니다.

  • BOOK_SALES → 2022년 1월 판매량(SALES) 제공
  • BOOK → 도서별 판매가(PRICE)와 카테고리(CATEGORY) 제공
  • AUTHOR → 저자 ID에 대응하는 저자명(AUTHOR_NAME) 제공

💡 Tip. 매출액 = 판매량(SALES) × 판매가(PRICE)인데, 두 컬럼이 각각 BOOK_SALESBOOK에 흩어져 있습니다. JOIN 후 곱셈을 수행해야 한다는 점을 기억하세요.


코드

ver(1) - CTE + JOIN 활용

핵심 아이디어: CTE로 BOOK_SALES에서 2022년 1월 도서별 총 판매량을 먼저 집계한 뒤, BOOKAUTHOR 테이블을 JOIN하여 저자별·카테고리별 매출액 합계를 계산합니다. 쿼리를 “판매량 집계 → 매출액 계산” 두 단계로 분리해 가독성이 높은 풀이입니다.

-- CTE: 2022년 1월의 도서별 총 판매량을 미리 집계
WITH MonthlySales AS (
    SELECT
        BOOK_ID,
        SUM(SALES) AS TOTAL_AMOUNT       -- 도서별 1월 총 판매량 합산
    FROM BOOK_SALES
    WHERE SALES_DATE LIKE '%2022-01%'    -- 2022년 1월 데이터만 필터링
    GROUP BY BOOK_ID                     -- 도서 ID별로 묶어서 집계
)

-- 메인 쿼리: CTE, BOOK, AUTHOR를 JOIN하여 저자별·카테고리별 매출액 산출
SELECT
    B.AUTHOR_ID,
    A.AUTHOR_NAME,
    B.CATEGORY,
    SUM(M.TOTAL_AMOUNT * B.PRICE) AS TOTAL_SALES  -- 판매량 × 판매가 = 매출액 합계
FROM MonthlySales AS M
JOIN BOOK   B ON M.BOOK_ID   = B.BOOK_ID    -- 판매 데이터 ↔ 도서 정보 연결
JOIN AUTHOR A ON B.AUTHOR_ID = A.AUTHOR_ID  -- 도서 정보 ↔ 저자 정보 연결
GROUP BY B.AUTHOR_ID, B.CATEGORY            -- 저자별, 카테고리별로 묶어 집계
ORDER BY B.AUTHOR_ID ASC, B.CATEGORY DESC; -- 저자 ID 오름차순, 카테고리 내림차순

💡 Tip. CTE를 쓰는 이유 날짜 필터링과 판매량 집계를 CTE에 분리해두면 두 가지 이점이 있습니다.

  1. 성능: 메인 쿼리가 JOIN하기 전에 데이터 행 수가 줄어 처리 비용이 감소합니다.
  2. 가독성: “1월 판매량 집계 → 매출액 계산”의 흐름이 명확하게 보입니다.


ver(2) - 인라인 뷰(서브쿼리) + JOIN 활용

핵심 아이디어: CTE 대신 FROM 절 안에 서브쿼리를 직접 작성하는 인라인 뷰 방식입니다. CTE와 논리적으로 동일하지만, WITH 문 없이 한 쿼리 안에 모두 담고 싶을 때 사용합니다.

-- FROM 절의 서브쿼리(인라인 뷰)가 CTE 역할을 대신함
SELECT
    B.AUTHOR_ID,
    A.AUTHOR_NAME,
    B.CATEGORY,
    SUM(M.TOTAL_AMOUNT * B.PRICE) AS TOTAL_SALES  -- 판매량 × 판매가 = 매출액 합계
FROM (
    -- 인라인 뷰: 2022년 1월 도서별 총 판매량 집계
    SELECT
        BOOK_ID,
        SUM(SALES) AS TOTAL_AMOUNT  -- 도서별 1월 총 판매량 합산
    FROM BOOK_SALES
    WHERE SALES_DATE LIKE '%2022-01%'  -- 2022년 1월 데이터만 필터링
    GROUP BY BOOK_ID                   -- 도서 ID별로 묶어서 집계
) AS M
JOIN BOOK   B ON M.BOOK_ID   = B.BOOK_ID    -- 인라인 뷰 ↔ 도서 정보 연결
JOIN AUTHOR A ON B.AUTHOR_ID = A.AUTHOR_ID  -- 도서 정보 ↔ 저자 정보 연결
GROUP BY B.AUTHOR_ID, B.CATEGORY            -- 저자별, 카테고리별로 묶어 집계
ORDER BY B.AUTHOR_ID ASC, B.CATEGORY DESC; -- 저자 ID 오름차순, 카테고리 내림차순

💡 Tip. CTE vs 인라인 뷰 두 방식은 실행 결과가 동일합니다. CTE(WITH)는 쿼리 상단에 미리 정의하므로 재사용이 가능하고 가독성이 좋습니다. 인라인 뷰는 별도의 WITH 절 없이 FROM 절 안에 바로 작성하므로 쿼리가 짧을 때 간결하게 쓸 수 있습니다.


ver(3) - JOIN만으로 한 번에 처리

핵심 아이디어: CTE나 서브쿼리 없이 3개 테이블을 바로 JOIN한 뒤, WHERE로 날짜를 필터링하고 GROUP BY로 집계합니다. 가장 짧고 단순한 풀이이지만, BOOK_SALES의 날짜별 행을 JOIN 단계에서 모두 펼친 후 집계하므로 데이터가 많을수록 처리 비용이 늘어날 수 있습니다.

-- 3개 테이블을 바로 JOIN하고 WHERE + GROUP BY로 한 번에 집계
SELECT
    B.AUTHOR_ID,
    A.AUTHOR_NAME,
    B.CATEGORY,
    SUM(BS.SALES * B.PRICE) AS TOTAL_SALES  -- 날짜별 판매량 × 판매가를 합산
FROM BOOK_SALES BS
JOIN BOOK   B ON BS.BOOK_ID  = B.BOOK_ID    -- 판매 데이터 ↔ 도서 정보 연결
JOIN AUTHOR A ON B.AUTHOR_ID = A.AUTHOR_ID  -- 도서 정보 ↔ 저자 정보 연결
WHERE BS.SALES_DATE LIKE '%2022-01%'        -- 2022년 1월 데이터만 필터링
GROUP BY B.AUTHOR_ID, B.CATEGORY            -- 저자별, 카테고리별로 묶어 집계
ORDER BY B.AUTHOR_ID ASC, B.CATEGORY DESC; -- 저자 ID 오름차순, 카테고리 내림차순

⚠️ 주의. ver(1), ver(2)는 먼저 판매량을 도서별로 합산한 뒤 PRICE를 곱합니다. ver(3)은 JOIN 후 날짜별 행 각각에 PRICE를 곱한 뒤 SUM으로 합산합니다. 수학적으로는 결과가 동일하지만(SUM(a × c) = SUM(a) × c, 단 c가 상수일 때), JOIN 규모 면에서는 ver(1), ver(2)가 더 효율적입니다.


📊 풀이법 비교 요약

  ver(1) CTE + JOIN ver(2) 인라인 뷰 + JOIN ver(3) JOIN만 활용
가독성 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
성능 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐
코드 길이 보통 보통 짧음
재사용성 ✅ CTE 재사용 가능
MySQL 버전 8.0+ 권장 모든 버전 모든 버전

💡 어떤 풀이를 써야 할까?

  • 코딩 테스트에서는 ver(3)이 가장 빠르게 작성할 수 있어 실용적입니다.
  • 실무나 복잡한 쿼리에서는 ver(1)처럼 CTE로 단계를 분리하면 유지보수가 쉽습니다.
  • ver(2)는 MySQL 구버전 환경에서 CTE를 쓸 수 없을 때 대안으로 사용합니다.

📊 쿼리 실행 흐름 요약 (ver(1) 기준)

BOOK_SALES
  └─ WHERE 2022-01 필터링
  └─ GROUP BY BOOK_ID → SUM(SALES)
          ↓ CTE: MonthlySales
     JOIN BOOK   → CATEGORY, PRICE, AUTHOR_ID 확보
          ↓
     JOIN AUTHOR → AUTHOR_NAME 확보
          ↓
     GROUP BY AUTHOR_ID, CATEGORY
          ↓
     SUM(TOTAL_AMOUNT × PRICE) = TOTAL_SALES
          ↓
     ORDER BY AUTHOR_ID ASC, CATEGORY DESC

댓글남기기