작성
·
12
0
WITH base AS (
SELECT
DISTINCT
DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul') AS event_datetime,
DATE(DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul')) AS event_date,
DATE_TRUNC(DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul'), WEEK(MONDAY)) AS event_week,
DATE_TRUNC(DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul'), MONTH) AS event_month,
user_id,
user_pseudo_id,
event_name,
platform,
event_params
FROM advanced.app_logs
), user_visit_base AS (
SELECT
user_pseudo_id,
MIN(event_week) OVER (PARTITION BY user_pseudo_id) AS first_visit_week,
event_week AS visit_week
FROM base
), user_visit_weekdiff AS (
SELECT
first_visit_week,
DATE_DIFF(visit_week, first_visit_week, week) AS week_diff,
COUNT(DISTINCT user_pseudo_id) AS visit_users
FROM user_visit_base
GROUP BY ALL
)
SELECT
first_visit_week,
week_diff,
visit_users,
SAFE_DIVIDE(visit_users, FIRST_VALUE(visit_users) OVER (PARTITION BY first_visit_week ORDER BY week_diff ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) AS retention
FROM user_visit_weekdiff
-- 한 달(30일)을 기준으로 구분하였다.
-- 신규 유저: 최근 한 달 이내 새로 방문
-- 기존 유저: 최근 한 달 이내 재방문, 그 이전 한달에도 방문
-- 복귀 유저: 최근 한 달 이내 재방문, 그 이전 한달에는 방문 X
-- 휴먼 유저: 최근 한 달 이내 재방문 X
WITH base AS (
SELECT
DISTINCT
DATE(DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul')) AS event_date,
user_pseudo_id
FROM advanced.app_logs
), user_first_last AS (
SELECT
user_pseudo_id,
MIN(event_date) AS first_date,
MAX(event_date) AS last_date
FROM base
GROUP BY user_pseudo_id
), last_event_date AS (
SELECT
MAX(event_date) AS current_date
FROM base
), user_activity AS (
SELECT
user_pseudo_id,
event_date,
LEAD(event_date) OVER (PARTITION BY user_pseudo_id ORDER BY event_date) AS next_event_date,
CASE
WHEN DATE_DIFF(LEAD(event_date) OVER (PARTITION BY user_pseudo_id ORDER BY event_date), event_date, DAY) > 30 THEN 1 ELSE 0
END AS dormant_history
FROM base
), user_types AS (
SELECT
u.user_pseudo_id,
u.first_date,
u.last_date,
led.current_date,
MAX(us.dormant_history) AS dormant_history,
CASE
WHEN DATE_DIFF(led.current_date, u.last_date, DAY) > 30 THEN 'dormant_user'
WHEN DATE_DIFF(u.last_date, u.first_date, DAY) <= 30 THEN 'new_user'
WHEN MAX(us.dormant_history) = 1 THEN 'resurrected_user'
ELSE 'current_user'
END AS user_type
FROM user_first_last AS u
CROSS JOIN last_event_date AS led
LEFT JOIN user_activity AS us ON u.user_pseudo_id = us.user_pseudo_id
GROUP BY u.user_pseudo_id, u.first_date, u.last_date, led.current_date
), first_week_and_diff AS (
SELECT
ut.user_type,
fw.user_pseudo_id,
fw.event_date,
DATE_DIFF(DATE_TRUNC(fw.event_date, WEEK(MONDAY)), DATE_TRUNC(ut.first_date, WEEK(MONDAY)), WEEK) AS diff_of_week
FROM base AS fw
JOIN user_types AS ut ON fw.user_pseudo_id = ut.user_pseudo_id
), user_cnt_by_type_and_week AS (
SELECT
user_type,
diff_of_week,
COUNT(DISTINCT user_pseudo_id) AS user_cnt
FROM first_week_and_diff
GROUP BY user_type, diff_of_week
), retention_base AS (
SELECT
user_type,
diff_of_week,
user_cnt,
FIRST_VALUE(user_cnt) OVER (PARTITION BY user_type ORDER BY diff_of_week) AS first_user_cnt
FROM user_cnt_by_type_and_week
)
SELECT
user_type,
diff_of_week,
ROUND(SAFE_DIVIDE(user_cnt, first_user_cnt), 2) AS retention_rate
FROM retention_base
ORDER BY user_type, diff_of_week
current user: 최고 0.41 (4~5주차)
new user: 최고 0.11 (1,3주차)
resurrected user: 최고 0.14 (9~12주차)
dormant user: 최고 0.09 (3주차)
리텐션이 그나마 높은 그룹: current user
current user, resurrected user 리텐션 분포의 경우 상승세 → 하강세 경향성을 보여 피크를 찍을 때 즈음 어떤 이벤트가 있었다고도 추측할 수 있다.
WITH base AS (
SELECT
DISTINCT
DATE(DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul')) AS event_date,
DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul') AS event_datetime,
event_name,
user_id,
user_pseudo_id
FROM advanced.app_logs
WHERE 1=1
AND event_name = "click_payment"
), event_week_and_first_week AS (
SELECT
DISTINCT
DATE_TRUNC(event_date, WEEK(MONDAY)) AS event_week,
user_pseudo_id,
DATE_TRUNC(MIN(event_date) OVER(PARTITION BY user_pseudo_id ORDER BY event_date), WEEK(MONDAY)) AS first_week
FROM base
), retention_base AS (
SELECT
*,
FIRST_VALUE(user_cnt) OVER(ORDER BY day_of_week) AS total_user
FROM (
SELECT
DATE_DIFF(event_week, first_week, WEEK) AS day_of_week,
COUNT(DISTINCT user_pseudo_id) AS user_cnt
FROM event_week_and_first_week
GROUP BY day_of_week
)
)
SELECT
day_of_week,
ROUND(SAFE_DIVIDE(user_cnt, total_user), 2) AS retention
FROM retention_base
ORDER BY day_of_week
답변 1
0
안녕하세요, 인프런 AI 인턴입니다. 김성호님의 질문에 대해 유사한 답변을 찾으려고 노력했습니다. 아래는 관련된 기존의 질문들입니다.
리텐션 관련 질문 및 코드 개선:
리텐션 분석과 세그멘테이션:
위의 링크들에서는 김성호님께서 작성하신 쿼리와 유사한 맥락에서 문제를 다루고 있으며, 리텐션을 주차별로 계산하는 방법과 유저 세그멘테이션을 통한 분석 방법을 공유하고 있습니다. 특히, 리텐션이 높은 유저 그룹을 분석하거나 특정 이벤트 중심의 리텐션을 계산하는 부분에 주목하시면 도움이 될 것입니다.
제가 제공한 정보를 통해 김성호님께서 의도하신 리텐션 분석과 코드 개선에 도움이 되길 바랍니다. 이 링크들을 참고하시면 문제 해결에 더 가깝게 다가설 수 있을 것입니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.