문제 1
- 테이블 설명 :
students 테이블은 학생에 대한 정보를 담고 있습니다. 테이블 구조와 각 컬럼의 의미는 다음과 같습니다.
컬럼명 타입 설명
STUDENT_ID | INT | 학생 ID (PK) |
NAME | VARCHAR | 학생 이름 |
GENDER | CHAR(1) | 성별 (M/F) |
AGE | INT | 나이 |
SCORE | DECIMAL | 시험 점수 |
- 분석해야 할 내용은 다음과 같습니다 :
각 성별(GENDER) 기준으로 시험 점수가 높은 상위 3명의 학생 성별, 이름과 점수를 반환하는 SQL 문을 작성하세요.
두 학생이 동점일 경우, 나이가 많은 학생을 우선합니다. 결과는 성별(GENDER) 오름차순, 순위 오름차순으로 정렬하여 출력하세요.
출력 값 예시
students 테이블이 다음과 같다면 :
STUDENT_ID NAME GENDER AGE SCORE
1 | Alice | F | 20 | 95 |
2 | Bob | M | 25 | 90 |
3 | Charlie | M | 23 | 92 |
4 | Diana | F | 19 | 85 |
5 | Eve | F | 22 | 93 |
6 | Frank | M | 22 | 90 |
7 | Grace | F | 21 | 89 |
- 성별이 F인 경우, 시험 점수가 높은 상위 3명은 Alice(95), Eve(93), Grace(89)입니다.
- 성별이 M인 경우, 시험 점수가 높은 상위 3명은 Charlie(92), Bob(90), Frank(90)입니다.
- Bob 과 Frank는 동점이지만, Bob이 나이가 더 많아 먼저 출력 됩니다.
정답 코드
with student_ranks as (
select *, rank() over (partition by gender order by score desc, age desc) student_rank
from qcc.students
)
select GENDER, NAME, SCORE
from student_ranks
where student_rank <= 3
order by gender, student_rank
문제 2
- 테이블 설명 :
books 테이블은 도서에 대한 정보를 담고 있습니다. 테이블 구조와 각 컬럼의 의미는 다음과 같습니다.
컬럼명 타입 설명
ID | INT | 도서 ID (PK) |
TITLE | VARCHAR(255) | 도서 제목 |
AUTHOR | VARCHAR(255) | 저자 |
PRICE | DECIMAL(8,2) | 가격 |
COPIES_IN_STOCK | INT | 재고 수량 |
book_orders 테이블은 주문에 대한 정보를 담고 있습니다. 테이블 구조와 각 컬럼의 의미는 다음과 같습니다.
컬럼명 타입 설명
ID | INT | 주문 ID (PK) |
CUSTOMER_ID | INT | 고객 ID |
ORDER_DATE | DATETIME | 주문 날짜 |
DUE_DATE | DATETIME | 결제 기한 |
PAID_DATE | DATETIME | 결제 완료 날짜 |
book_order_items 테이블은 주문 항목에 대한 정보를 담고 있습니다. 테이블 구조와 각 컬럼의 의미는 다음과 같습니다.
컬럼명 타입 설명
ID | INT | 주문 항목 ID (PK) |
ORDER_ID | INT | 주문 ID (FK) |
BOOK_ID | INT | 도서 ID (FK) |
QUANTITY | INT | 수량 |
PRICE | DECIMAL(8,2) | 개별 가격 |
LINE_TOTAL | DECIMAL(8,2) | 총 가격 (수량 * 가격) |
- 분석해야 할 내용은 다음과 같습니다 :
모든 도서에 대해 도서 제목(TITLE)과 다음 정보를 반환하는 SQL 쿼리를 작성하세요 :
- 미결제 금액 (DUE): 아직 결제되지 않은 총 금액을 계산합니다.
- 계산 기준 : PAID_DATE 가 NULL인 주문 항목의 총 금액 합계
- 결과는 반올림하여 정수로 반환하세요.
- 결제 완료 금액 (PAID): 결제 완료된 총 금액
- 계산 기준 : PAID_DATE 가 NULL이 아닌 주문 항목의 총 금액 합계
- 결과는 반올림하여 정수로 반환하세요.
결과는 도서 제목(TITLE)을 기준으로 오름차순 정렬하세요.
출력 값 예시
books , book_orders , book_order_items 테이블이 다음과 같다면 :
books
ID TITLE AUTHOR PRICE COPIES_IN_STOCK
1 | The Great Gatsby | F. Scott | 10.00 | 20 |
2 | To Kill a Mockingbird | Harper Lee | 12.00 | 15 |
3 | 1984 | George Orwell | 15.00 | 30 |
book_orders
ID CUSTOMER_ID ORDER_DATE DUE_DATE PAID_DATE
1 | 101 | 2023-01-01 | 2023-01-07 | 2023-01-05 |
2 | 102 | 2023-01-10 | 2023-01-15 | NULL |
3 | 103 | 2023-01-20 | 2023-01-25 | NULL |
4 | 104 | 2023-01-30 | 2023-02-05 | NULL |
book_order_items
ID ORDER_ID BOOK_ID QUANTITY PRICE LINE_TOTAL
1 | 1 | 1 | 2 | 10.00 | 20.00 |
2 | 2 | 1 | 1 | 10.00 | 10.00 |
3 | 3 | 2 | 3 | 12.00 | 36.00 |
4 | 4 | 3 | 1 | 15.00 | 15.00 |
- The Great Gatsby:
- 주문 1: 결제 완료 (PAID_DATE가 존재하므로 PAID = $20)
- 주문 2: 미결제 (PAID_DATE가 NULL이므로 DUE = $10)
- To Kill a Mockingbird:
- 주문 3: 미결제 (PAID_DATE가 NULL이므로 DUE = $36)
- 결제 금액 없음 (PAID = $0)
- 1984
- 주문 4: 미결제 (PAID_DATE가 NULL이므로 DUE = $15)
- 결제 금액 없음 (PAID = $0)
정답 코드
SELECT
b.TITLE,
ROUND(COALESCE(SUM((o.PAID_DATE IS NULL) * oi.LINE_TOTAL), 0), 0) AS DUE,
ROUND(COALESCE(SUM((o.PAID_DATE IS NOT NULL) * oi.LINE_TOTAL), 0), 0) AS PAID
FROM qcc.books b
LEFT JOIN qcc.book_order_items oi ON b.ID = oi.BOOK_ID
LEFT JOIN qcc.book_orders o ON oi.ORDER_ID = o.ID
GROUP BY b.ID, b.TITLE
ORDER BY b.TITLE ASC;
문제 3
- 테이블 설명
customer_orders 테이블은 고객의 주문 데이터를 포함하고 있습니다. 테이블 구조와 각 컬럼의 의미는 다음과 같습니다.
컬럼명 타입 설명
ORDER_ID | INT | 주문 ID (PK) |
CUSTOMER_ID | INT | 고객 ID |
ORDER_DATE | DATE | 주문 날짜 |
ORDER_AMOUNT | DECIMAL(10,2) | 주문 금액 |
- 분석해야 할 내용은 다음과 같습니다 :
고객의 첫 주문 월을 기준으로 Cohort 그룹을 만들고, 각 Cohort 그룹에서 시간이 지남에 따라 활성 사용자 수를 계산하는 SQL 문을 작성하세요.
USER_COUNT_1_MONTH_LATER ~ USER_COUNT_12_MONTH_LATER 까지 계산해야 합니다.
- 각 Cohort 그룹에 대해 1개월 후부터 12개월 후까지의 활성 사용자 수를 추적합니다.
- Cohort 정의: FIRST_ORDER_MONTH
- First Order Month: 각 고객의 첫 주문이 발생한 월을 의미합니다.
- COHORT_USER_COUNT:
- 각 Cohort 그룹의 [활성 사용자] 수를 계산합니다.
- **[활성 사용자]**는 주문을 한 고객을 의미합니다.
- 각 Cohort 그룹의 [활성 사용자] 수를 계산합니다.
- USER_COUNT_X_MONTHS_LATER:
- 해당 COHORT가 X개월 이후에 주문한 고객 수를 계산합니다.
- 예:
- FIRST_ORDER_MONTH = 2023-01
- COHORT_USER_COUNT = 50
- USER_COUNT_1_MONTH_LATER = 20
- USER_COUNT_2_MONTH_LATER = 33
- 2023-01 에 첫 주문을 한 고객이 50명, 이들 중 1개월 뒤 주문한 고객이 20명, 2개월 뒤 주문한 고객이 33명
출력 값 예시
customer_orders 테이블이 다음과 같다면 :
ORDER_ID CUSTOMER_ID ORDER_DATE ORDER_AMOUNT
1 | 101 | 1/1/23 | 120.5 |
2 | 102 | 1/15/23 | 200 |
3 | 101 | 2/10/23 | 100 |
4 | 103 | 2/5/23 | 75 |
5 | 104 | 3/1/23 | 50 |
6 | 102 | 3/12/23 | 150 |
Cohort 2023-01 :
- COHORT_USER_COUNT: 2명 (CUSTOMER_ID 101, 102).
- USER_COUNT_1_MONTH_LATER: 1명 (CUSTOMER_ID 101이 2023-02에 다시 주문)
- USER_COUNT_2_MONTH_LATER: 1명 (CUSTOMER_ID 102가 2023-03에 다시 주문)
Cohort 2023-02 :
- COHORT_USER_COUNT: 1명 (CUSTOMER_ID 103)
- USER_COUNT_1_MONTH_LATER: 1명 (CUSTOMER_ID 103이 2023-03에 다시 주문)
- USER_COUNT_2_MONTH_LATER: 0명 (2023-04에 재구매 없음)
Cohort 2023-03 :
- COHORT_USER_COUNT: 1명 (CUSTOMER_ID 104)
- 이후 활동 없음
정답 코드
WITH cohort AS (
SELECT
CUSTOMER_ID,
DATE(DATE_FORMAT(MIN(ORDER_DATE), '%Y-%m-01')) AS first_order_month
FROM customer_orders
GROUP BY CUSTOMER_ID
), active_orders AS (
SELECT
o.CUSTOMER_ID,
c.first_order_month,
DATE(DATE_FORMAT(o.ORDER_DATE, '%Y-%m-01')) AS active_month
FROM customer_orders o
JOIN cohort c
ON o.CUSTOMER_ID = c.CUSTOMER_ID
), cohort_counts AS (
SELECT
first_order_month,
active_month,
COUNT(DISTINCT CUSTOMER_ID) AS user_count
FROM active_orders
GROUP BY first_order_month, active_month
)
SELECT
DATE_FORMAT(first_order_month, '%Y-%m') FIRST_ORDER_MONTH,
COALESCE(SUM(CASE WHEN active_month = first_order_month THEN user_count ELSE 0 END), 0) AS COHORT_USER_COUNT,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 1 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_1_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 2 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_2_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 3 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_3_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 4 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_4_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 5 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_5_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 6 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_6_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 7 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_7_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 8 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_8_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 9 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_9_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 10 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_10_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 11 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_11_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 12 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_12_MONTH_LATER
FROM cohort_counts
GROUP BY first_order_month
ORDER BY first_order_month;
코드 해석
SELECT
CUSTOMER_ID,
DATE(DATE_FORMAT(MIN(ORDER_DATE), '%Y-%m-01')) AS first_order_month
FROM customer_orders
GROUP BY CUSTOMER_ID
(1) `cohort` 테이블
• 고객별로 첫 주문이 발생한 달(`first_order_month`)을 계산합니다.
• `MIN(ORDER_DATE)`를 사용하여 각 고객의 첫 주문 날짜를 구하고, 이를 월 단위로 변환합니다.
SELECT
o.CUSTOMER_ID,
c.first_order_month,
DATE(DATE_FORMAT(o.ORDER_DATE, '%Y-%m-01')) AS active_month
FROM customer_orders o
JOIN cohort c
ON o.CUSTOMER_ID = c.CUSTOMER_ID
(2) `active_orders` 테이블
• 각 주문(`ORDER_DATE`)에 대해 고객의 첫 주문 달(`first_order_month`)과 현재 주문 달(`active_month`)을 계산합니다.
• `customer_orders`와 `cohort`를 조인하여 각 주문에 첫 주문 달을 추가합니다.
SELECT
first_order_month,
active_month,
COUNT(DISTINCT CUSTOMER_ID) AS user_count
FROM active_orders
GROUP BY first_order_month, active_month
(3) `cohort_counts` 테이블
• 각 코호트(`first_order_month`)와 활동한 달(`active_month`)에 대해 고유한 고객 수(`user_count`)를 계산합니다.
SELECT
DATE_FORMAT(first_order_month, '%Y-%m') FIRST_ORDER_MONTH,
COALESCE(SUM(CASE WHEN active_month = first_order_month THEN user_count ELSE 0 END), 0) AS COHORT_USER_COUNT,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 1 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_1_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 2 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_2_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 3 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_3_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 4 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_4_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 5 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_5_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 6 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_6_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 7 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_7_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 8 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_8_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 9 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_9_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 10 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_10_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 11 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_11_MONTH_LATER,
COALESCE(SUM(CASE WHEN active_month = DATE_ADD(first_order_month, INTERVAL 12 MONTH) THEN user_count ELSE 0 END), 0) AS USER_COUNT_12_MONTH_LATER
FROM cohort_counts
GROUP BY first_order_month
ORDER BY first_order_month;
최종 SELECT 절
최종적으로, 각 코호트의 첫 주문 달(`first_order_month`)을 기준으로 최대 12개월 동안 활동한 고객 수를 계산하여 반환합니다.
주요 내용:
1. `DATE_FORMAT(first_order_month, '%Y-%m') FIRST_ORDER_MONTH`:
• 코호트의 첫 주문 달을 `YYYY-MM` 형식으로 반환합니다.
2. 월별 활동 고객 수 계산:
• `CASE WHEN active_month = first_order_month THEN user_count ELSE 0 END`:
• 활동한 달이 첫 주문 달과 같으면 해당 고객 수를 포함하고, 그렇지 않으면 포함하지 않습니다.
• 이를 반복적으로 작성하여 첫 주문 후 최대 12개월 동안의 활동 고객 수를 각각 계산합니다.
• `DATE_ADD(first_order_month, INTERVAL n MONTH)`를 사용해 특정 월을 동적으로 계산합니다.
3. `COALESCE`:
• 결과가 `NULL`일 경우 `0`으로 대체하여 반환합니다.
4. GROUP BY 및 ORDER BY:
• 코호트의 첫 주문 달(`first_order_month`)별로 그룹화하고, 오름차순 정렬합니다.
'[업무 지식] > MySQL' 카테고리의 다른 글
[QCC] Query Challenge Cycle (0) | 2025.01.03 |
---|---|
[recursive] Print Prime Numbers (0) | 2024.12.13 |
[recursive] Draw The Triangle (0) | 2024.12.12 |
[날짜 그룹화] SQL Project Planning (0) | 2024.12.03 |
[MAX() over()] Challenges (0) | 2024.11.29 |