์ด๋ฒ์๊ฐ์๋ SQL์ JOIN ์ฐ์ฐ์ ๋ํด ์์๋ณด์.
0) JOIN ์ด๋
๋ ๊ฐ ์ด์์ ํ ์ด๋ธ์ ํน์ ์กฐ๊ฑด์ ๋ง์ถฐ์ merge ํ๋ ๊ฒ.
์ฐ์ , ๋ ๊ฐ์ ํ ์ด๋ธ์ด๋ผ๊ณ ์๊ฐํ๊ณ ์ผ์ชฝ ํ ์ด๋ธ์ left, ์ค๋ฅธ์ชฝ ํ ์ด๋ธ์ right์ด๋ผ๊ณ ํ๋ค.
1) ํ ์ด๋ธ์ ๊ด๊ณ
1:1
์์ 1:1์ธ ํ ์ด๋ธ์ด๊ฑฐ๋, ํ ํ ์ด๋ธ์ด ๋ค๋ฅธ ํ ์ด๋ธ์ ๋ถ๋ถ ์งํฉ์ธ ๊ฒฝ์ฐ์ด๋ค.
๋ณดํต, join ์ฐ์ฐ ์ ํฌ๊ฒ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๋๋ค.
1:๋ค
join ์ฐ์ฐ์ ๊ธฐ์ค์ ์ ๋ชป ์ก์ผ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
๋ค:๋ค
๊ฐ๋ฅํ๋ฉด join ์ฐ์ฐ์ ์ํํ์ง ์๊ณ ๋ค:1, 1:1, 1:๋ค ํ ์ด๋ธ๋ก ๋ณํํด์ ์ฌ์ฉํ๋๋ก ํ๋ค.
โป ์ด๋ ํ ์ด๋ธ์ ๊ธฐ์ค์ผ๋ก ์กฐ์ธํ ์ง๋ฅผ ์ ์ค์ ํด์ผ ํ๋ค. โป
2) 6๊ฐ์ง JOIN ์ฐ์ฐ
INNER JOIN
LEFT, RIGHT ํ ์ด๋ธ์ ๊ต์งํฉ์ธ ๋ ์ฝ๋
FULL JOIN
LEFT, RIGHT ํ ์ด๋ธ์ ํฉ์งํฉ์ธ ๋ ์ฝ๋(๋งค์นญ์ด ๋ ๊ฒฝ์ฐ MERGE, ๋งค์นญ๋์ง ์๋ ๊ฐ์ ๋ํด์๋ NULL)
CROSS JOIN
LEFT, RIGHT ํ ์ด๋ธ์ ๋ชจ๋ ๋ ์ฝ๋์ ์กฐํฉ
LEFT JOIN
LEFT ํ ์ด๋ธ ๊ธฐ์ค์ผ๋ก ๋งค์นญํ์ฌ MERGE
RIGHT JOIN
RIGHT ํ ์ด๋ธ ๊ธฐ์ค์ผ๋ก ๋งค์นญํ๋ฉฐ MERGE (๋ณดํต LEFT JOIN์ ์ฌ์ฉ)
SELF JOIN
๋์ผํ ํ ์ด๋ธ๋ก ์กฐ์ธ์ ์ํ
์ฝ๋
SELECT ~
FROM ํ
์ด๋ธ A ์กฐ์ธ์ด๋ฆJOIN ํ
์ด๋ธ B ON A.์ปฌ๋ผ๋ช
=B.์ปฌ๋ผ๋ช
WHERE ~
GROUP BY ~
HAVING ~
ORDER BY ~
LIMIT ~
โป ๊ทธ๋ฐ๋ฐ ์์ ํ์ตํ ๋ฐ์ดํฐ ์จ์ดํ์ฐ์ค์ ํน์ง ์ค์ Primary key์ unique๊ฐ ๋ณด์ฅ์ด ์๋๋ค๋ ํญ๋ชฉ์ด ์์๋ค.
๋ฐ๋ผ์ ์กฐ์ธ ์ฐ์ฐ์ ์ํ ํ๊ธฐ ์ ์ ๋ฏธ๋ฆฌ Primary key ๋ฐ์ดํฐ์ ์ค๋ณต์ ์ฒดํฌํด์ผ ํ๋ค. โป
2) ์ค์ต
์์๋ก ์์ง๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ์กฐ์ธ ์ฐ์ฐ์ ์ํํด ๋ณด์.
raw_data
- vital (UserID, VitalID, Date, Weight) : ์ฌ์ฉ์๊ฐ ํ๋ฃจ์ ํ ๋ฒ์ฉ ์ธก์ ํด์ ์ ์ฅ
- alert (UserID, VitalID, AlertType, Date, UserID) : ์ฌ์ฉ์์ ํ๋(์ ๋ณด)์ ๋ฐ๋ผ ๋ฐ์ํ๋ ์๋, ์ฌ์ฉ์๊ฐ 2์ผ ์ด์ ๋ฐ์ดํ์ ๊ธฐ๋กํ์ง ์๋ ๊ฒฝ์ฐ VitalID NULL๋ก AlterType MissingType์ด ๋ฐ์ํ๋ค.
VitalID๋ฅผ ๊ธฐ์ค์ผ๋ก ์กฐํํ๋ค.
1. InnerJoin
(NULL)๊ฐ์ ์ ์ธํ๊ณ ๊ณตํต์ผ ๊ฒฝ์ฐ merge๋ฅผ ์ํ
SELECT ~
FROM VITAL A
JOIN ALERT B ON A.VITALID=B.VITALID;
2. LeftJoin/RightJoin
(์ผ์ชฝ ๋ชจ๋ ๋ ์ฝ๋์ ๋ํด) ๊ฐ์ด ๊ฐ์ ๋ ์ฝ๋๊ฐ ์๋ ๊ฒฝ์ฐ merge, ์๋ ๊ฒฝ์ฐ null
SELECT ~
FROM VITAL A
LEFT JOIN ALERT B ON A.VITALID=B.VITALID;
3. FullJoin
(์ผ์ชฝ, ์ค๋ฅธ์ชฝ ๋ชจ๋ ๋ ์ฝ๋์ ๋ํด) ๊ฐ์ด ๊ฐ์ ๋ ์ฝ๋๊ฐ ์๋ ๊ฒฝ์ฐ merge, ์๋ ๊ฒฝ์ฐ null
SELECT ~
FROM VITAL A
FULL JOIN ALERT B ON A.VITALID=B.VITALID;
4. CrossJoin
์ผ์ชฝ, ์ค๋ฅธ์ชฝ ๋ ์ฝ๋์ ๋ชจ๋ ์กฐํฉ์ ์(์ผ์ชฝ ๋ ์ฝ๋ ์ X์ค๋ฅธ์ชฝ ๋ ์ฝ๋ ์
SELECT ~
FROM VITAL A
CROSS JOIN ALERT B ON A.VITALID=B.VITALID;
5. SelfJoin
๊ฐ์ ํ ์ด๋ธ์ ๋ํด alias๋ง ๋ค๋ฅด๊ฒ ํด์ ์กฐ์ธ
SELECT ~
FROM VITAL A
JOIN VITAL B ON A.VITALID=B.VITALID;
๋ณดํต ํด๋น ํํ๋ณด๋ค, ๋ค๋ฅธ ์กฐ๊ฑด๋ค์ด ์ถ๊ฐ๋๋ค.
3) SQL ์ ์์ฌํญ
1. BOOLEAN๊ฐ ์ฒ๋ฆฌํ๊ธฐ(True, False)
๋์ผํ ํํ
์ปฌ๋ผ=True
์ปฌ๋ผ is True
๋ค๋ฅธ ํํ(์ปฌ๋ผ๊ฐ์ด True, False์ธ์ ๋ค๋ฅธ ๊ฐ์ ๊ฐ์ง ์ ์๋ค. ex.null๊ฐ)
์ปฌ๋ผ is True
์ปฌ๋ผ is not False
2. NULL๊ฐ ์ฒ๋ฆฌํ๊ธฐ
null๊ฐ์ ๋น๊ต์ฐ์ฐ์ ๋์ IS NULL, IS NOT NULL ์ด์ฉํ๋ค.
NULLIF(์ปฌ๋ผ๊ฐ, ๊ฐ) : ๊ฐ → NULL
๋๋์ ์ ๊ฒฝ์ฐ 0์ผ๋ก ๋๋๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ฏ๋ก NULLIF ํจ์๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋ค.
NULLIF(์ปฌ๋ผ๊ฐ, ๊ฐ) → ์ปฌ๋ผ๊ฐ=๊ฐ์ด๋ฉด NULL์ ๋ฐํ ๊ทธ๋ ์ง ์์ผ๋ฉด ์ปฌ๋ผ ๋ฐํ
COALESCE(๊ฐ 1, ๊ฐ 2,...) : NULL → ๊ฐ
์ ์ผ ์ฒ์ NULL์ด ์๋ ๊ฐ์ return(๋๊น์ง NULL์ด๋ฉด NULL์ ๋ฐํ)
3. ์์ฝ๋ ํค์๋/๊ณต๋ฐฑ์ ์ปฌ๋ผ ์ด๋ฆ์ผ๋ก ์ฌ์ฉํ๊ณ ์ถ์ ๋
ํฐ ๋ฐ์ดํ๋ฅผ ์ด์ฉํ๋ค.
CREATE TABLE ํ
์ด๋ธ๋ช
(
"์ปฌ ๋ผ ๋ช
" ์๋ฃํ,
"์์ฝ์ด" ์๋ฃํ
);
๋์ค์ ์ฌ์ฉํ ๋๋ ํฐ๋ฐ์ดํ๋ฅผ ๋ถ์ฌ์ ์ฌ์ฉํด์ผ ํ๋ค.
4) ์ด์ ์์ - ์๋ณ ์ฑ๋๋ณ ๋งค์ถ์ก ํ ์ด๋ธ ๋ง๋ค๊ธฐ ์ฝ๋ ์์ ํ๊ธฐ
SQL
SELECT
TO_CHAR(T.ts, 'YYYY-MM') AS month,
channel,
COUNT(DISTINCT U.userid) as uniqueUsers, --ํ ์ฌ์ฉ์๋ ํ ๋ฒ๋ง count
Count(DISTINCT CASE WHEN TR.amount >=0 THEN U.userid END)as paidUsers, --ํ ์ฌ์ฉ์๋ ํ ๋ฒ๋ง count
ROUND(paidUsers AS Float*100.0/NULLIF(uniqueUsers, 0), 2) as conversionRate,
Sum(CASE WHEN TR.refunded is NOT NULL then Tr.amount END) as grossRevenue,
Sum(CASE WHEN TR.refunded=False then Tr.amount ELSE 0 END) as netRevenue
FROM raw_data.user_session_channel U
JOIN raw_data.session_timestamp T ON T.sessionid=U.sessionid
LEFT JOIN raw_data.session_transaction TR ON T.sessionid=TR.sessionid
GROUP BY 1, 2
ORDER BY 1, 2;
- channel ํ ์ด๋ธ ์กฐ์ธ ํด์
user_session_channel ํ ์ด๋ธ์ ์ฑ๋ ์ด๋ฆ ์กด์ฌ
- paidUsers์ distinct userid ์ฌ์ฉ
ํ ์ฌ๋์ด ์ฌ๋ฌ ๋ฒ ๊ตฌ๋งคํ ์ ์์ผ๋ฏ๋ก
- conversionRate์ typecasting ๋์ ์ค์ํ ๊ณฑ์
์ค์X์ ์=์ค์
- ๋๋์ ์ฐ์ฐ์ NULLIF ํจ์ ์ฌ์ฉ
0์ผ๋ก ๋๋ ์ ์์ผ๋ฏ๋ก NULL์ ๋ฐํํ๋๋ก ํ๋ค.
CTAS ์ฌ์ฉํด์ redshift์ ์๋ก์ด ํ ์ด๋ธ ์์ฑ
%%sql
DROP TABLE IF EXISTS ์คํค๋ง.session_summary;
CREATE TABLE ์คํค๋ง.session_summary AS
๊ทธ ํ์, ์์ SQL ๋ฌธ์ฅ์ ์ฌ์ฉํ๋ค.(CTAS์์๋ orderby ๋ถ๋ถ์ ์ ์ฉ๋์ง ์๋๋ค)
5) ์ค๋ ์์
1. ์ฌ์ฉ์๋ณ ์ฒ์ ์ ์ํ ์ฑ๋, ๋ง์ง๋ง ์ฑ๋ ํ์ธํ๊ธฐ
- first_value, last_value ์ด์ฉ
%%sql
SELECT
distinct usc.userid,
FIRST_VALUE(usc.channel) OVER (PARTITION BY usc.userid ORDER BY st.ts
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS FirstValue,
LAST_VALUE(usc.channel) OVER (PARTITION BY usc.userid ORDER BY st.ts
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS LastValue
FROM raw_data.user_session_channel usc
JOIN raw_data.session_timestamp st ON usc.sessionid = st.sessionid;
2. Gross Revenue๊ฐ ๊ฐ์ฅ ํฐ UserID 10๊ฐ ํ์ธํ๊ธฐ
%%sql
SELECT
usc.userid,
SUM(amount) gross_revenue
FROM raw_data.user_session_channel usc
LEFT JOIN raw_data.session_transaction str ON usc.sessionid = str.sessionid
where str.amount>0
GROUP BY 1
ORDER BY 2 desc
limit 10;
3. nps ํ ์ด๋ธ์ ๋ฐํ์ผ๋ก ์๋ณ NPS ๊ณ์ฐํ๊ธฐ
%%sql
SELECT LEFT(created_at, 7) AS month,
ROUND(SUM(
CASE
WHEN score >= 9 THEN 1
WHEN score <= 6 THEN -1
END)*100.00/COUNT(1), 2)
FROM raw_data.nps
GROUP BY 1
ORDER BY 1;