2025. 7. 22. 17:46ใTools & Skills/SQL
๐ ๋น์ง๋์ค ๋ฌธ์ ์๋๋ฆฌ์ค
์ต๊ทผ ์๊ฐ ํ์ฑ ์ฌ์ฉ์(MAU)๋ ์์ ์ ์ด์ง๋ง, ์ ๊ท ๊ฐ์ ์์ 2์ฃผ์ฐจ ์ฌ๋ฐฉ๋ฌธ์จ์ด 20% ์ดํ๋ก ๋จ์ด์ง
PM์ "์ถ์ฒ ์ํ ๋ ธ์ถ ๋ฐฉ์"์ด ๋ฌธ์ ๋ผ๊ณ ๊ฐ์ค์ ์ธ์ ์ง๋ง,
๋ง์ผํ ํ์ "์ ๊ท ์ฌ์ฉ์ ์จ๋ณด๋ฉ ํ๋ก์ธ์ค์ ๋ถ์กฑ"์ด ๋ ํฐ ๋ฌธ์ ๋ผ๊ณ ์ฃผ์ฅํจ.
ํ์ฌ ํ์ ํต์ฌ ๊ณผ์ ๋ ๋ฆฌํ ์ (์ฌ์ฉ์ ์ฌ๋ฐฉ๋ฌธ) ๊ฐ์ ์ ๋๋ค.
๋ฏธ์ :
- ๋ฆฌํ ์ ๋ถ์์ ํตํด 2์ฃผ์ฐจ ์ฌ๋ฐฉ๋ฌธ์จ์ด ๋ฎ์ ์์ธ์ ๊ท๋ช ํ๋ ์ฝ๋ ์์ฑ๊ณผ,
- ์์ฑํ SQL์ ์ต์ ํ๋ ๋ฐฉ์์ผ๋ก ์์ฑํ์ฌ ๋ถ์ ์๋๋ฅผ ๋์ด๋ฉฐ,
- ์ต์ข ์ ์ผ๋ก ์คํ ๊ณํ์ ์์ธก → ์ตํฐ๋ง์ด์ ๊ด์ ์์ ์ฑ๋ฅ์ ์ ๊ฒํด์ผ ํจ
โก๏ธ Step 1. ๋ฆฌํ ์ ๋ถ์ ๊ธฐ๋ณธ ํ๋ ์ ์ค๊ณ
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด ๋์๊ฐ ๋ณด์.
โ ๋ฆฌํ ์ ์ ์ด๋ค ๋ฐฉ์์ผ๋ก ์ ์ํ ๊ฒ์ธ๊ฐ? (์ฝํธํธ vs ๋น์ฝํธํธ)
๋ฆฌํ ์ ์ ๊ธฐ๋ณธ์ ์ผ๋ก “ํน์ ์์ ์ดํ ๋ค์ ๋์์จ ์ฌ์ฉ์ ๋น์จ”์ด๊ณ ,
์ด๋ฅผ ์ด๋ค ๋จ์๋ก ์ธก์ ํ ์ง์ ๋ฐ๋ผ ๋ฐฉ์์ด ๋ฌ๋ผ์ง๋ค.
๊ทธ๋ ๋ค๋ฉด ๋์ ธ์ผ ํ๋ ์ง๋ฌธ์?
๋๊ตฌ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ธก์ ํ ๊ฒ์ธ๊ฐ?
A: ๊ฐ์ ์ผ(๋๋ ์ฒซ ๋ฐฉ๋ฌธ์ผ)์ ๊ธฐ์ค์ผ๋ก ์ผ๋๋ค → “์ ๊ท ์ฌ์ฉ์ ๋ฆฌํ ์ ”
B: ํน์ ์ด๋ฒคํธ(์: ๊ตฌ๋งค, ์ฅ๋ฐ๊ตฌ๋ ์ถ๊ฐ)๋ฅผ ๊ธฐ์ค์ผ๋ก ์ผ๋๋ค → “์ด๋ฒคํธ ๊ธฐ๋ฐ ๋ฆฌํ ์ ”
C: ์ ์ฒด ํ์ฑ ์ฌ์ฉ์ ๋์ → “์ ์ฒด ์ ์ ์์กด์จ”
→ ํ์ฌ ๋น์ง๋์ค ๋ชฉํ๋ ์ ๊ท ๊ฐ์ ์์ 2์ฃผ์ฐจ ์ฌ๋ฐฉ๋ฌธ์จ์ด๋, A๊ฐ ์ ํฉํ ๊ฐ๋ฅ์ฑ์ด ๋๋ค.
๋ฆฌํ ์ ์ ๋ฉฐ์น ๋จ์๋ก ๋ณผ๊น?
์ ํ์ง A: ์ผ ๋จ์ → D1(๋ค์๋ ), D7(7์ผ์ฐจ), D14(14์ผ์ฐจ), …
์ ํ์ง B: ์ฃผ ๋จ์ → W1(1์ฃผ์ฐจ), W2(2์ฃผ์ฐจ), …
์ ํ์ง C: ์ ๋จ์ → M1(1๊ฐ์์ฐจ), M2(2๊ฐ์์ฐจ), …
→ ์ด๋ฐ์๋ ์ผ ๋จ์๋ก ์ธ๋ฐํ ๋ณธ ๋ค์ ๋์ค์ ๋ฌถ๋ ์ ๋ต์ ์ ํํด๋ณด์.
๋๊ฐ ์์กด(๋ฆฌํ ์ )ํ๋ค๊ณ ๋ณผ๊น?
์ ํ์ง A: ์ฑ์ ํ ๋ฒ์ด๋ผ๋ ์ ์ํ๋ฉด ์์กด์ผ๋ก ๋ณธ๋ค (๋จ์ ๋ฐฉ๋ฌธ ๋ฆฌํ ์ )
์ ํ์ง B: ํน์ ํ๋(๊ตฌ๋งค, ์ฅ๋ฐ๊ตฌ๋ ์ถ๊ฐ ๋ฑ)์ ํ๋ฉด ์์กด์ผ๋ก ๋ณธ๋ค (ํ๋ ๊ธฐ๋ฐ ๋ฆฌํ ์ )
→ ๋จ์ ๋ฐฉ๋ฌธ์ผ๋ก ๋ฆฌํ ์ ์ ์ง๊ณํ๊ฒ ๋๋ฉด ํ์ฑํ ์ฌ์ฉ์ ์๋ฅผ ๊ณผ๋ํ๊ฒ ์ธก์ ํ ์ ์์ผ๋ฉฐ,
์๋ณ์ด ๋ถ๊ฐํ ์ฌ์ฉ์๋ค๋ก ์ธํด ๋ฆฌํ ์ ์ ๊ณ์ฐํ๋ ๋ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ์๋ค.
๋ฐ๋ผ์, ํน์ ํ๋์ ํ ์ ์ ์ ๋ํด ์์กดํ๋ค๊ณ ๋ณธ๋ค.
๋ฆฌํ ์ ์ ์ฝํธํธ๋ณ๋ก ๋ณผ๊น์, ์๋๋ฉด ์ ์ฒด ํ๊ท ์ผ๋ก ๋ณผ๊น?
์ ํ์ง A: ์ฝํธํธ ๋ฆฌํ ์ → ๊ฐ์ ์ฃผ์ฐจ๋ณ๋ก ๋ฌถ์ด์ ๋ณธ๋ค. (์: 7์ ์ฒซ์งธ์ฃผ ๊ฐ์ ์ vs ๋์งธ ์ฃผ ๊ฐ์ ์ ๋น๊ต)
์ ํ์ง B: ๋น์ฝํธํธ ๋ฆฌํ ์ → ์ ์ฒด ํ๊ท ์ ์ผ๋ก D7=20%, D14=10%์ฒ๋ผ ํ ๋์ ๋ณธ๋ค
→ PM๊ณผ ๋ง์ผํ ํ์ด “์ ๋ฎ์์ง ์์ธ ๊ท๋ช ”์ ์ํ๋ฏ๋ก, ์ฝํธํธ ๋ฆฌํ ์ ์ด ์ ๋ฆฌํ ๊ฒ์ผ๋ก ์์
โก๏ธ Step 2. ํ ์ด๋ธ ๊ตฌ์กฐ ์ค๊ณ
๋ฆฌํ ์ ๋ถ์์ ํ์ํ ์ต์ ํ ์ด๋ธ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒ์ด๋ค.
๐ ์ ์ ํ ์ด๋ธ (users)
user_id | signup_date |
101 | 2025-07-01 |
102 | 2025-07-03 |
103 | 2025-07-05 |
๐ ์ ์ ํ๋ ํ ์ด๋ธ (user_events)
→ ๋ฆฌํ ์ ์ ์๊ฐ “ํน์ ํ๋ ๊ธฐ๋ฐ”์ด๋ฏ๋ก ๋ค์ ๊ตฌ์กฐ๊ฐ ํ์ํ ๊ฒ์ด๋ค.
user_id | event_date | event_type |
101 | 2025-07-02 | view_product |
101 | 2025-07-05 | purchase |
102 | 2025-07-10 | add_to_cart |
103 | 2025-07-14 | purchase |
โก๏ธ Step 3. ์ฝํธํธ ๋ฆฌํ ์ SQL ์ด์
์ ํ ์ด๋ธ ๊ตฌ์กฐ์ ๋ง์ถฐ SQL์ ์์ฑํด๋ณด์.
-- 1) ๊ธฐ๋ณธ ์ฝํธํธ ๋ฆฌํ
์
๊ณ์ฐ
SELECT
WEEK(u.signup_date) AS cohort_week, -- ๊ฐ์
์ฃผ์ฐจ
DATEDIFF(e.event_date, u.signup_date) AS days_since_signup, -- ๊ฐ์
ํ n์ผ์ฐจ
COUNT(DISTINCT u.user_id) AS retained_users
FROM users u
JOIN user_evnts e
ON u.user_id = e.user_id
AND e.event_type IN ('purchase', 'add_to_chart') -- ํ๋ ๊ธฐ๋ฐ ๋ฆฌํ
์
AND DATEDIFF(e.event_type, u.signup_date) BETWEEN 1 AND 14 -- 14์ผ์ฐจ๊น์ง
GROUP BY cohort_week, days_since_signup
ORDER BY cohort_week, days_since_signup;
โ SQL์ด ๋ฐํํ๋ ๋ฆฌํ ์ ํ ์์
| cohort_week | days_since_signup | retained_users |
| ------------ | ------------------- | --------------- |
| 2025-06-30 | 1 | 50 |
| 2025-06-30 | 7 | 20 |
| 2025-06-30 | 14 | 10 |
| 2025-07-07 | 1 | 80 |
| 2025-07-07 | 7 | 30 |
๐ค SQL ํ๋ ๊ด์ ์์ ์๊ฐํด ๋ณผ ํฌ์ธํธ๋ ์ด๋ค ๊ฒ๋ค์ด ์์๊น?
๋จ๊ณ๋ณ๋ก ์ง๋ฌธ ํด๋ณด์.
1. ์ SQL์ ์คํํ๋ฉด ์ตํฐ๋ง์ด์ ๋ ์ด๋ค ์คํ ๊ณํ์ ์ ํํ ๊น?
users์ user_events๋ u.user_id = e.user_id๋ผ๋ ๋๋ฑ ์กฐ์ธ(=)์ผ๋ก ์ฐ๊ฒฐ๋๋ฉฐ,
์ตํฐ๋ง์ด์ ๋ ์ ํ๋(Selectivity)๊ฐ ๋ ์ข์ ์ชฝ(์ฆ, ํํฐ๋ง์ผ๋ก ๋ฐ์ดํฐ๊ฐ ๋ ์ค์ด๋๋ ์ชฝ)์ ๋จผ์ ์์ธ์ค ํ๋ ค ํ ๊ฒ์ด๋ค.
์ถ๊ฐ๋ก ํ์ฅํ๋ค๋ฉด
์ตํฐ๋ง์ด์ ๋ ์กฐ์ธ ์์๋ฅผ ์ ํํ ๋ users์ row ์(๊ฐ์ ์ ์)์ user_events์ ์ด๋ฒคํธ ๋ฐ์ ์๋ฅผ ๋น๊ตํด
๋น์ฉ์ด ๋ ๋ฎ์ ์ชฝ์ ๋๋ผ์ด๋น ํ ์ด๋ธ(๋จผ์ ์ฝ๋ ํ ์ด๋ธ)๋ก ์ ํํ๋ค.
์ง๊ธ ์ฟผ๋ฆฌ์์๋ user_events๊ฐ ์์ฒ๋ง ๊ฑด์ด๋ผ๋ฉด,
์ตํฐ๋ง์ด์ ๋ users → user_events ์์ผ๋ก ์งํํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ผ๋ก ์ ๋ฆฌํ๋ค.
์ด๋ u.signup_date๊ฐ JOIN ์ด์ ์ ๋จผ์ ์ฝํธํธ ๊ธฐ์ค์ผ๋ก ํํฐ๋ง๋๋ฉด,
์ดํ user_events๋ฅผ ์กฐ์ธํ ๋ user_id ํ๋ณด ์๊ฐ ํจ์ฌ ์ค์ด๋ ๋ค.
1. users์์ cohort_week, signup_date ํํฐ -> ํ๋ณด user_id ์ถ์ถ
2. ํ๋ณด user_id๋ง user_events์์ ํ์ -> IN (user_id_list)
2. e.event_type๊ณผ DATEDIFF ์กฐ๊ฑด์ ์ธ๋ฑ์ค ์ฌ์ฉ์ด ๊ฐ๋ฅํ๊ฐ?
event_type ์ปฌ๋ผ์ ์ธ๋ฑ์ค๋ฅผ ์์ฑํ๋ฉด MySQL ์ตํฐ๋ง์ด์ ๋
IN ์ ์ ๋ช ์๋ ๊ฐ๋ค์ ์ฌ์ฉํ์ฌ ํด๋น ๋ ์ฝ๋๋ฅผ ๋น ๋ฅด๊ฒ ์ฐพ์ ์ ์๋ค.
ALTER TABLE user_events ADD INDEX idx_event_type (event_type);
๊ทธ๋ฌ๋, DATEDIFF()์ ๊ฐ์ด ์ปฌ๋ผ์ ํจ์๋ฅผ ์ ์ฉํ ๊ฒฐ๊ณผ๊ฐ์ ๋ํด์๋ ์ผ๋ฐ์ ์ธ ์ธ๋ฑ์ค๋ฅผ ์ง์ ์ฌ์ฉํ ์ ์๋ค.
์ธ๋ฑ์ค๋ ์ปฌ๋ผ์ ์๋ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ์ ๋ ฌ๋์ด ์๊ธฐ ๋๋ฌธ์,
์ฟผ๋ฆฌ ์คํ ์๋ง๋ค ํจ์๊ฐ ๊ณ์ฐ๋๋ฏ๋ก, ์ธ๋ฑ์ค๋ ๊ฐ์ ํ์ฉํ ์ ์๋ค.
๊ทธ๋ผ ์ด๋กํ์ฃ ?
event_date ์์ฒด๋ฅผ ํ์ฉํ๋ ๋ฒ์ ์กฐ๊ฑด์ผ๋ก ๋ณํํ์ฌ ๊ฐ์ ์ ์ผ๋ก ํ์ฉํ ์ ์๋ค.
e.event_date BETWEEN DATE_ADD(u.signup_date, INTERVAL 1 DAY)
AND DATE_ADD(u.signup_date, INTERVAL 14 DAY)
3. ๋ง์ฝ user_events๊ฐ ์์ฒ๋ง ๊ฑด์ด๋ผ๋ฉด, ์ด SQL์ ์ฑ๋ฅ ๋ณ๋ชฉ์ ์ด๋์์ ์๊ธธ๊น?
์ตํฐ๋ง์ด์ ๋ 2๊ฐ์ง์ ๋ณ๋ชฉ์ ๊ฒช๋๋ค.
1. user_events์ Full-Scan ์ํ
JOIN ์์๊ฐ ์๋ชป ์กํ๊ฑฐ๋ DATEDIFF๋ก ์ธํด ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ง ๋ชปํ๋ฉด,
์ตํฐ๋ง์ด์ ๊ฐ user_events๋ฅผ Full-scan ํด์ผํ๋ค.
2. GROUP BY ๋น์ฉ
-- JOIN ์ดํ
GROUP BY cohort_week, days_since_signup
GROUP BY๋ JOIN ์ดํ ์ํ๋๋ฏ๋ก, JOIN ๊ฒฐ๊ณผ๊ฐ ์ปค์ง๋ฉด GROUPING ๋น์ฉ์ด ๊ธ์ฆํ๋ค.
๐ก ๋ณ๋ชฉ ์ํ ์ ๋ต
1. ์ฌ์ ํํฐ๋ง
users์์ 2์ฃผ ์ด๋ด ๊ฐ์ ์๋ง ๋จผ์ ํํฐ๋งํด ๋๋ผ์ด๋น ํ ์ด๋ธ์ ํฌ๊ธฐ๋ฅผ ์ค์ธ๋ค.
-- 1, ์ฌ์ ํํฐ๋ง์ ํตํ ํ๋
-- ์กฐ์ธ ์ ์ ๊ฐ ํ
์ด๋ธ์ ๋จผ์ ํํฐ๋งํ์ฌ ์ฒ๋ฆฌํ ๋ฐ์ดํฐ๋ ๊ฐ์
SELECT
WEEK(filtered_users.signup_date) AS cohort_week,
DATEDIFF(filtered_events.event_date, filtered_users.signup_date) AS days_since_signup,
COUNT(DISTINCT filtered_users.user_id) AS retained_users
FROM (
SELECT user_id, signup_date
FROM users
WHERE signup_date >= DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
) filtered_users
JOIN (
-- ์ด๋ฒคํธ ํ
์ด๋ธ ์ฌ์ ํํฐ๋ง (๊ด๋ จ ์ด๋ฒคํธ๋ง)
SELECT user_id, event_date
FROM user_events
WHERE event_type IN ('purchase', 'add_to_cart')
AND event_date >= DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
) filtered_events ON filtered_users.user_id = filtered_events.user_id
WHERE DATEDIFF(filtered_events.event_date, filtered_users.signup_date) BETWEEN 1 AND 14
GROUP BY cohort_week, days_since_signup
ORDER BY cohort_week, days_since_signup
2. ์ธ๋ฑ์ค ์ฌ์ค๊ณ
user_events์ (event_type, event_date, user_id) ๋ณตํฉ ์ธ๋ฑ์ค๋ฅผ ๊ณ ๋ คํ๋ค.
-- 2. ์ธ๋ฑ์ค ์ฌ์ค๊ณ๋ฅผ ํตํ ํ๋
-- user ํ
์ด๋ธ ์ธ๋ฑ์ค
CREATE INDEX idx_users_signup_date_id ON users(signup_date, user_id);
-- user_events ํ
์ด๋ธ ์ธ๋ฑ์ค
CREATE INDEX idx_events_type_date_user ON user_events(event_type, event_date, user_id);
CREATE INDEX idx_events_user_type_date ON user_events(user_id, event_type, event_date);
-- ์ธ๋ฑ์ค ํ์ฉ ์ต์ ํ๋ ์ฟผ๋ฆฌ
SELECT
WEEK(u.signup_date) AS cohort_week,
DATEDIFF(e.event_date, u.signup_date) AS days_since_signup,
COUNT(DISTINCT u.user_id) AS retained_users
FROM users u
INNER JOIN user_events e ON u.user_id = e.user_id
WHERE u.signup_date >= DATE_SUB(CURDATE(), INTERVAL 3 MONTH) -- ์ธ๋ฑ์ค ํ์ฉ
AND e.event_type IN('purchase', 'add_to_cart') -- ์ธ๋ฑ์ค ํ์ฉ
AND e.event_date >= u.signup_date -- ๋ถํ์ํ ๊ณผ๊ฑฐ ์ด๋ฒคํธ ์ ์ธ
AND e.event_date <= DATE_ADD(u.signup_date, INTERVAL 14 DAY) -- ๋ฒ์ ์ต์ ํ
GROUP BY cohort_week, days_since_signup
ORDER BY cohort_week, days_since_signup;
3. Subquery๋ก ์ด๋ฒคํธ ํํฐ๋ง ํ ์กฐ์ธ
์ตํฐ๋ง์ด์ ๊ฐ ๊ฐ์ ๋ก ํํฐ ์์๋ฅผ ๋ฐ๊พธ๊ฒ๋ ์ ๋ํ๋ ๋ฐฉ๋ฒ๋ ์๋ค.
-- 3. ์๋ธ์ฟผ๋ฆฌ๋ก ์ด๋ฒคํธ ํํฐ๋ง ํ ์กฐ์ธํ๋ ํ๋
-- ๊ด๋ จ ์ด๋ฒคํธ๊ฐ ์๋ ์ฌ์ฉ์๋ง ๋จผ์ ์๋ณํ ํ ์กฐ์ธ
WITH cohort_users AS (
-- Step 1: ์ฝํธํธ๋ณ ์ฌ์ฉ์ ๊ธฐ๋ณธ ์ ๋ณด
SELECT
user_id,
signup_date,
WEEK(signup_date) AS cohort_week
FROM users
WHERE signup_date >= DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
),
relevant_events AS (
-- Step 2: ๋ฆฌํ
์
๊ด๋ จ ์ด๋ฒคํธ๋ง ์ถ์ถ
SELECT
e.user_id,
e.event_date
FROM user_events e
INNER JOIN cohort_users cu ON e.user_id = cu.user_id
WHERE e.event_type IN ('purchase', 'add_to_cart')
AND e.event_date > cu.signup_date -- ๊ฐ์
์ผ ์ดํ๋ง
AND e.event_date <= DATE_ADD(cu.signup_date, INTERVAL 14 DAY) -- 14์ผ ์ด๋ด๋ง
),
retention_data AS (
-- Step 3: ๋ฆฌํ
์
๋ฐ์ดํฐ ๊ณ์ฐ
SELECT
cu.cohort_week,
cu.user_id,
DATEDIFF(re.event_date, cu.signup_date) AS days_since_signup
FROM cohort_users cu
INNER JOIN relevant_events re ON cu.user_id = re.user_id
)
-- Step 4: ์ต์ข
์ง๊ณ
SELECT cohort_week,
days_since_signup,
COUNT(DISTINCT user_id) AS retained_users
FROM retention_data
GROUP BY cohort_week, days_since_signup
ORDER BY cohort_week, days_since_signup;
์ด๋ ๊ฒ ๋ณ๋ชฉ ์ํ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ค๋ฉด,
๊ฐ์ ๋ ์ฑ๋ฅ์ ์ธก์ ํ๋ ์ฟผ๋ฆฌ๋ ์์ฑํด ํ์ธํด์ผ ํ๋ค.
-- ์ฑ๋ฅ ์ธก์ ๋ฐ ๋ชจ๋ํฐ๋ง ์ฟผ๋ฆฌ
-- ์คํ ๊ณํ ํ์ธ
EXPLAIN EXTENDED [์์ ํ๋๋ ์ฟผ๋ฆฌ ์ค ํ๋];
-- ์ธ๋ฑ์ค ์ฌ์ฉ๋ฅ ํ์ธ
SHOW INDEX FROM users;
SHOW INDEX FROM user_events;
-- ํ
์ด๋ธ ํต๊ณ ์
๋ฐ์ดํธ
ANALYZE TABLE users, user_events;
์ตํฐ๋ง์ด์ ๊ฐ ์คํ๊ณํ์ ์ธ์ธ ๋ ์ตํฐ๋ง์ด์ ์๊ฒ ์ค์ํ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋
์นดํ๋ก๊ทธ ๋งค๋์ (ํ ์ด๋ธ ํต๊ณ)๋ฅผ ๋ฐ๋์ ์ ๋ฐ์ดํธ ํด์ผ ํ๋ค.
๋ง์ฝ ์ตํฐ๋ง์ด์ ๊ฐ ์๋ชป๋ ์คํ ๊ณํ์ ์ ํํ์ ๋ ์ตํ์ ์๋จ์ผ๋ก ์ฌ์ฉํ๋ 'ํํธ ๊ตฌ'๋ผ๋ ๊ฒ๋ ์กด์ฌํ๋ค.
์๋ฅผ ๋ค์ด, ์ตํฐ๋ง์ด์ ๊ฐ user_events๋ฅผ ๋จผ์ ๋๋ผ์ด๋น ํ ์ด๋ธ๋ก ์ ํํด ํ์ค์บํ๋ค๋ฉด,
ํํธ ๊ตฌ๋ก users๋ฅผ ๋จผ์ ์ฝ๊ฒ ๊ฐ์ ํ ์ ์๋ค.
-- LEADING(u e) -> u(users)๋ฅผ ๋จผ์ ์ฝ๊ณ e(user_events)๋ฅผ ์กฐ์ธํ๋ผ๋ ์๋ฏธ
SELECT /*+ LEADING(u e) */
DATE_TRUNC('week', u.signup_date) AS cohort_week, ...
FROM users u
JOIN user_events e
ON u.user_id = e.user_id
...
'Tools & Skills > SQL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ฐ๋ณต๋ฌธ ์์กด์ฆ (5) | 2025.08.03 |
---|---|
์ ์ฐจ ์งํฅ์์ ์ ์ธํ์ผ๋ก (3) | 2025.08.01 |
[SQL] ํ๋ก๊ทธ๋๋จธ์ค Lv.4 ๋ณดํธ์์์ ์ค์ฑํํ ๋๋ฌผ (0) | 2025.04.05 |
[SQL] LeetCode #511 - Game Play Analysis (0) | 2025.03.19 |
์ ๊ทํ (0) | 2025.03.19 |