arpu DESC, channel ASC같은 매출을 전교생으로 나누면 ARPU, 돈 낸 애들로만 나누면 ARPPU. 분자는 같고 분모만 다르다.
| 분자 | 분모 | |
|---|---|---|
| ARPU | 총 결제액 | 전체 가입자 |
| ARPPU | 총 결제액 | 결제한 사람 |
가입자 100명, 결제자 20명, 매출 200만원이면 → ARPU = 2만원, ARPPU = 10만원. 다섯 배 차이가 난다.
COUNT(DISTINCT u.user_id), 결제자는 COUNT(DISTINCT p.user_id).p.user_id 는 빈칸이고, COUNT(DISTINCT) 는 빈칸을 안 세기 때문이다.* 1.0, ROUND(…, 2).AVG(금액) 은 둘 다 아니다. 그건 결제 건당 평균이다. 한 사람이 다섯 번 결제하면 그 사람이 다섯 번 반영된다. '1인당' 을 물었으면 분모를 사람 수로 직접 써야 한다.AVG 는 빈칸인 줄을 분모에서도 뺀다. 값이 10, 빈칸, 20 이면 AVG 는 15 다. 10 이 아니다. 그래서 AVG 로는 ARPU 가 절대 안 나온다.COALESCE(…, 0) 을 씌운다.묻는 것 · 분모가 다른 두 평균을 한 쿼리에서 만든다. 실무에서 가장 자주 틀리는 지표 쌍이고, 면접에서도 묻는다.
users LEFT JOIN payments 로 결제 안 한 유저까지 붙인다.COUNT(DISTINCT u.user_id) 와 COUNT(DISTINCT p.user_id) 로 따로 센다. LEFT JOIN 이라 결제 없는 유저의 p.user_id 는 NULL 이고, COUNT(DISTINCT) 는 NULL 을 안 세므로 자동으로 결제 유저 수가 된다.* 1.0 을 붙이고 ROUND(..., 2).SELECT u.channel,
COUNT(DISTINCT u.user_id) AS users,
COUNT(DISTINCT p.user_id) AS payers,
ROUND(COALESCE(SUM(p.amount), 0) * 1.0
/ NULLIF(COUNT(DISTINCT u.user_id), 0), 2) AS arpu,
COALESCE(ROUND(SUM(p.amount) * 1.0
/ NULLIF(COUNT(DISTINCT p.user_id), 0), 2), 0) AS arppu
FROM users u
LEFT JOIN payments p ON p.user_id = u.user_id
GROUP BY u.channel
ORDER BY arpu DESC, u.channel ASC;AVG(p.amount) 는 ARPU 도 ARPPU 도 아니다. 결제 건당 평균이다. 한 유저가 5번 결제하면 그 유저가 5번 반영된다. '유저당' 을 물었으면 분모를 유저 수로 직접 써야 한다. 이 한 줄 차이로 숫자가 두 배 넘게 벌어진다.AVG 는 그 행을 분모에서 제외한다. 그래서 AVG(p.amount) 로는 ARPU 가 절대 안 나온다. 값이 10, NULL, 20 이면 AVG 는 15 지 10 이 아니다.COALESCE(SUM(...), 0) / 가입자수COALESCE(ROUND(...), 0)* 1.0 을 빼면 안 된다. amount 와 COUNT 가 둘 다 정수라 정수 나눗셈이 된다. 실제 토스 테스트에서 이걸로 틀린 적이 있다.arpu DESC 만 쓰면 동점에서 순서가 흔들린다.“ARPPU 대신 중앙값”이면 ROW_NUMBER + COUNT() OVER 패턴. “월별 ARPU 추이”면 GROUP BY 월, 채널 로 바꾸되 분모를 그 달의 활성 유저로 둘지 전체 가입자로 둘지를 문제에서 확인하라 — 여기서 정의가 갈린다.