문제 1: 월간 코호트 리텐션 (Retention & Cohort)
[난이도: 상]
문제: 사용자의 가입 월(Signup Month)을 기준으로 **'가입 후 1개월 뒤'와 '가입 후 2개월 뒤'의 리텐션(재방문율)**을 계산하세요.
- 리텐션의 정의: 가입 월에 가입한 전체 사용자 중, 해당 개월(M+1, M+2)에 events 테이블에 기록이 있는 사용자의 비율.
- 결과는 signup_month, month_1_retention, month_2_retention 형태로 출력하세요.
핵심 포인트:
- 가입 월별 기준점 설정 (Cohort)
- 사용자별 첫 방문일과 이후 방문일 사이의 간격 계산 (Date Diff)
- 분모(가입자 수)와 분자(재방문자 수)를 결합한 비율 계산
1. users (사용자 기본 정보)
| 컬럼명 | 타입 | 설명 |
| user_id | int | 유저 고유 ID (PK) |
| signup_date | date | 가입 일자 (예: '2024-01-01') |
| ab_group | varchar | A/B 테스트 그룹 ('A' 또는 'B') |
2. events (사용자 활동 로그)
이 테이블은 사용자가 서비스에 방문하거나 특정 행동을 할 때마다 기록됩니다.
| 컬럼명 | 타입 | 설명 |
| event_id | int | 이벤트 고유 ID (PK) |
| user_id | int | 유저 고유 ID (FK - users.user_id와 연결) |
| event_date | date | 이벤트 발생 일자 (예: '2024-02-15') |
| event_name | varchar | 이벤트 종류 (예: 'login', 'click' 등) |
출력 예시)
가입한 달에 대비하여, 다음 달(M+1)과 다다음 달(M+2)에 얼마나 돌아왔는지를 백분율로 보여줍니다.
| signup_month | cohort_size | m1_retention | m2_retention |
| 2024-01 | 1000 | 0.45 (45%) | 0.30 (30%) |
| 2024-02 | 1200 | 0.40 (40%) | 0.25 (25%) |
- 힌트: 가입월 유저수($Users_{M+0}$) 대비 재방문 유저수($Users_{M+n}$)의 비율을 구하세요.
내 풀이
/* 사용자의 가입 월을 기준으로,
가입 후 1개월 뒤와 가입 후 2개월 뒤의 리텐션(재방문율)을 계산
재방문율 =
signup_date가 가입월 +n이 event_date에 존재하는 회원 수/ 가입 월 기준 고유 전체 회원 수
결과는 signup_month, month_1_retention, month_2_retention형태로 출력.*/
WITH SINGUP_MONTH_USERS AS (
SELECT user_id, COUNT(user_id) AS total_users
FROM users),
ONE_MONTH_RETENTION AS (
SELECT A.user_id AS user_id,
(COUNT(*)) / (A.total_users) AS ONE_MONTH_RETENTION
FROM SINGUP_MONTH_USERS A
LEFT JOIN events B ON A.user_id = B.user_id
WHERE DATE_DIFF(mm,A.SIGNUP_MONTH,B.event_date) = 1
),
TWO_MONTH_RETENTION AS (
SELECT A.user_id AS user_id,
(COUNT(*)) / (A.total_users) AS TWO_MONTH_RETENTION
FROM SINGUP_MONTH_USERS A
LEFT JOIN events B ON A.user_id = B.user_id
WHERE DATE_DIFF(mm,A.SIGNUP_MONTH,B.event_date) = 2
)
SELECT A.total_users AS signup_month,
B.ONE_MONTH_RETENTION AS month_1_retention,
C.TWO_MONTH_RETENTION AS month_2_retention
FROM SINGUP_MONTH_USERS A
JOIN ONE_MONTH_RETENTION B ON A.user_id = B.user_id
JOIN TWO_MONTH_RETENTION C ON B.user_id = C.user_id;
문제1 - 월 별로 집계해야하는데 그렇지 않음.
최종 정답
WITH user_cohort AS (
-- 1. 각 유저의 가입월을 구하고, 가입월별 전체 유저 수(분모)를 미리 계산합니다.
SELECT
user_id,
DATE_FORMAT(signup_date, '%Y-%m-01') AS cohort_month
FROM users
),
cohort_size AS (
-- 2. 각 코호트(가입월)에 몇 명이 가입했는지 집계합니다.
SELECT
cohort_month,
COUNT(DISTINCT user_id) AS total_users
FROM user_cohort
GROUP BY cohort_month
),
user_activities AS (
-- 3. TIMESTAMPDIFF를 사용하여 가입월과 이벤트 발생월 사이의 개월 수 차이를 구합니다.
SELECT
u.cohort_month,
u.user_id,
TIMESTAMPDIFF(MONTH, u.cohort_month, DATE_FORMAT(e.event_date, '%Y-%m-01')) AS month_diff
FROM user_cohort u
JOIN events e ON u.user_id = e.user_id
)
-- 4. 최종 집계: 코호트 사이즈 대비 M+1, M+2 리텐션을 백분율로 산출합니다.
SELECT
c.cohort_month AS signup_month,
c.total_users AS cohort_size,
-- M+1 리텐션: 1개월 뒤 활동한 유니크 유저 수 / 전체 가입자 수
ROUND(COUNT(DISTINCT CASE WHEN a.month_diff = 1 THEN a.user_id END) / c.total_users, 4) AS month_1_retention,
-- M+2 리텐션: 2개월 뒤 활동한 유니크 유저 수 / 전체 가입자 수
ROUND(COUNT(DISTINCT CASE WHEN a.month_diff = 2 THEN a.user_id END) / c.total_users, 4) AS month_2_retention
FROM cohort_size c
LEFT JOIN user_activities a ON c.cohort_month = a.cohort_month
GROUP BY c.cohort_month, c.total_users
ORDER BY c.cohort_month;
문제 2: A/B 테스트 퍼널 전환율 분석 (Activation & A/B Test)
[난이도: 중상]
문제: 새로운 상세페이지 디자인(B 그룹)이 기존 디자인(A 그룹)보다 **'조회(view)에서 구매(order)로 이어지는 전환율'**이 얼마나 개선되었는지 분석하세요.
- 각 그룹별(A, B)로 view 이벤트를 발생시킨 고유 유저 수를 구하세요.
- 그 유저들 중 실제 orders 테이블에 결제 기록이 있는 고유 유저 수를 구하세요.
- 결과값은 ab_group, viewers, buyers, conversion_rate 순으로 출력하세요.
핵심 포인트:
- LEFT JOIN을 활용한 유저 단위 퍼널 분석
- 그룹별 집계 (GROUP BY)
- 실제 주문 데이터와 매칭 시점 확인
2. events (행동 로그 데이터)
| 컬럼명 | 타입 | 설명 |
| event_id | int | 로그 고유 ID |
| user_id | int | 유저 고유 ID (FK) |
| event_type | varchar | 행동 종류 ('view', 'cart', 'click') |
| event_timestamp | timestamp | 행동 발생 시각 (예: '2024-01-01 13:00:00') |
출력 예시)
단순 클릭이 아니라, '상품 조회(view)'를 한 사람 중 실제 '구매(order)'까지 이어진 비율을 비교합니다.
| ab_group | total_viewers | total_buyers | conversion_rate |
| A | 5000 | 250 | 0.050 (5.0%) |
| B | 4800 | 288 | 0.060 (6.0%) |
- 힌트: view 이벤트를 기록한 유저 리스트와 orders 테이블의 유저 리스트를 user_id 기준으로 결합하세요.
문제 3: 고가치 유저(Whale) 탐지 및 Revenue 분석 (Revenue)
[난이도: 상]
문제: 전체 매출의 상위 10%를 차지하는 **'핵심 유저군(Whales)'**의 평균 주문 금액(AOV)과 그들이 가장 많이 구매한 날짜를 찾으세요.
- 유저별 총 구매 금액을 합산하여 순위를 매깁니다.
- 누적 매출 비율이 상위 10% 이내에 드는 유저들만 필터링하세요.
- 이 유저들의 전체 평균 주문 금액(AVG(amount))을 구하세요.
핵심 포인트:
- 윈도우 함수(SUM() OVER)를 활용한 누적합 계산
- 퍼센트 계산을 위한 서브쿼리 활용
3. orders (결제 데이터)
| 컬럼명 | 타입 | 설명 |
| order_id | int | 주문 고유 ID |
| user_id | int | 유저 고유 ID (FK) |
| amount | decimal | 결제 금액 |
| order_timestamp | timestamp | 결제 완료 시각 |
출력 예시)
누적 매출 기여도가 높은 상위 10% 유저들의 구매 행태를 분석합니다.
| whale_avg_order_value | most_active_date | total_whale_count |
| 154,200 | 2024-12-25 | 150 |
- 힌트: 윈도우 함수 PERCENT_RANK()나 SUM(amount) OVER()를 사용하여 상위 10% 지점을 찾아보세요.
🚀 문제 4: 리텐션의 심화 - "첫 구매 후 재구매까지의 소요 기간(TT2P)"
[AARRR: Retention & Revenue]
단순한 접속 리텐션이 아니라, '첫 구매(First Order)'를 한 유저가 '두 번째 구매(Second Order)'를 하기까지 평균적으로 며칠이 걸리는지를 구하고자 합니다.
- 요구사항: 1. 유저별로 첫 번째 주문일과 두 번째 주문일을 각각 구하세요. (세 번째 이후 주문은 무시) 2. 첫 구매 후 30일 이내에 재구매한 유저의 비율(%)을 구하세요. 3. 재구매 유저들의 평균 재구매 소요 기간(Day)을 출력하세요.
- 활용 팁: ROW_NUMBER(), LEAD() 또는 LAG(), DATEDIFF()
- 출력 예시: | total_first_buyers | repurchase_rate_30d | avg_days_to_repurchase | | :--- | :--- | :--- | | 5000 | 0.28 (28%) | 14.5 |
테이블 - users (유저)
| 컬럼명 | 타입 | 설명 |
| user_id | int | 유저 고유 ID (PK) |
| signup_date | date | 가입일 (예: 2025-01-01) |
| ab_group | varchar | A/B 테스트 그룹 ('A' 또는 'B') |
🚀 문제 5: A/B 테스트의 심화 - "누적 매출 기여도(LTV 7D) 비교"
[AARRR: Activation & Revenue]
가입 후 첫 7일간의 누적 매출액을 통해 A/B 테스트 그룹 간의 질적 성과를 비교해야 합니다. 단순히 '누가 더 많이 샀나'가 아니라 **'누가 더 빨리, 꾸준히 샀나'**가 핵심입니다.
- 요구사항:
- 가입일로부터 7일 이내에 발생한 매출만 집계하세요.
- A그룹과 B그룹 각각에 대해, 가입자 1인당 평균 누적 매출(ARPU)을 일자별(D+0, D+1 ... D+7)로 구하세요.
- 윈도우 함수를 사용하여 일자별로 매출이 쌓이는 구조를 만드세요.
- 활용 팁: SUM() OVER(), DATE_ADD(), LEFT JOIN (가입했으나 구매하지 않은 유저도 포함해야 하므로 필수!)
- 출력 예시: | ab_group | day_since_signup | cumulative_revenue_per_user | | :--- | :--- | :--- | | A | 0 | 1200 | | A | 1 | 1500 | | ... | ... | ... | | B | 7 | 4500 |
테이블 - events (유저 행동 로그)
| 컬럼명 | 타입 | 설명 |
| event_id | int | 로그 고유 ID |
| user_id | int | 유저 고유 ID (FK) |
| event_type | varchar | 행동 종류 ('view', 'cart', 'search') |
| event_timestamp | timestamp | 행동 발생 시각 |
🚀 문제 6: 퍼널 분석의 심화 - "이탈 전 마지막 행동(Last Touch) 분석"
[AARRR: Activation]
장바구니(cart)에 상품을 담았지만, 최종 결제(order)를 하지 않고 이탈한 유저들을 찾고, 그들이 장바구니 담기 직전에 수행한 행동이 무엇인지 파악하려고 합니다.
- 요구사항:
- 최근 30일 이내에 cart 이벤트는 발생시켰으나, order 기록은 없는 유저를 추출하세요.
- 해당 유저들이 cart 이벤트를 발생시키기 직전 1개의 event_type이 무엇이었는지 찾으세요.
- 직전 행동별로 이탈 유저 수를 집계하여 내림차순 정렬하세요.
- 활용 팁: LAG() OVER(), CTE, NOT EXISTS 또는 EXCEPT
- 출력 예시: | last_event_before_cart | abandoned_user_count | | :--- | :--- | | search | 850 | | promotion_click | 420 | | direct_view | 150 |
테이블 - orders (주문/결제 정보)
| 컬럼명 | 타입 | 설명 |
| order_id | int | 주문 고유 ID |
| user_id | int | 유저 고유 ID (FK) |
| amount | decimal | 결제 금액 |
| order_timestamp | timestamp | 결제 시각 |
'SQLD' 카테고리의 다른 글
| SQL 9일차 #응용 문제 (0) | 2025.05.07 |
|---|---|
| SQL 8일차 (0) | 2025.05.02 |
| SQL 7일차 (0) | 2025.05.01 |
| SQL 6일차 #SQL 키트 + GPT 문제 (1) | 2025.05.01 |
| SQL 5일차 #LeetCodE (0) | 2025.04.27 |
































