Contents
1. JOIN이란?
2. 다양한 종류의 JOIN
3. 3일차 숙제 리뷰
4. 숙제
1. JOIN이란?
- SQL의 조인은 두 개 혹은 그 이상의 테이블들을 공통 필드를 가지고 머지하는데 사용된다.
- 이는 스타 스키마로 구성된 테이블들로 분산되어있던 정보를 통합하는데 사용된다.
- 왼쪽 테이블을 LEFT, 오른쪽 테이블을 RIGHT라고 하자.
- JOIN의 결과는 방식에 상관없이, 양쪽 필드를 모두 가진 새로운 테이블을 만들게 된다.
- JOIN의 방식에 따라 다음 두 가지가 달라진다.
- 어떤 레코드들이 선택되는지
- 어떤 레코드들이 채워지는지
- JOIN의 문법ex>
-
SELECT A.*, B.* FROM raw_data.table1 A ____JOIN raw_data.table2 B ON A.key1 =B.key1 and A.key2= B.key2 -- ____자리에 INNER, FULL, LEFT, RIGHT, CROSS 등이 오게 됨 WHERE A.ts >= '2019-01-01';
- JOIN시 고려해야할 점
- 먼저 중복 레코드가 없고, primary key uniqueness가 보장되는지 체크 (매우 중요)
- 조인하는 테이블 간 관계를 명확하게 정의해야함
- One to one (join되는 key가 테이블당 한 번씩 나오는 경우)
- 완전한 one to one : user_session_channel & session_timestamp (sessionId 한번씩- 어떤 join을 해도 동일한 결과)
- 한쪽이 부분집합이 되는 one to one: user_session_channel & session_transaction (key sessionId이지만, transaction은 일부이기 때문에, inner join과 left join의 결과물이 크게 다르다)
- One to many (order vs order_items)
- 이 경우 중복이 더 큰 문제가 됨 -> 증폭
- Many to one
- 방향만 바꾸면 one to many 로 보는 것과 사실상 동일
- Many to many
- 이런 경우는 많지 않으며 one to one이나 one to many로 바꿀 수 있다면 변환하여 조인하는 것이 덜 위험
- One to one (join되는 key가 테이블당 한 번씩 나오는 경우)
- 어느 테이블을 base로 잡을지(FROM에 사용할지) 결정해야 함
2. 다양한 종류의 JOIN
raw_data.Vital : 유저가 체중을 잴 때마다 기록되는 테이블
raw_data.Alert : 머신러닝을 통해 예측해서 문제 가능성이 있을 때 Alert을 만들어내고, 이를 기록하는 테이블
1. INNER JOIN
- 양쪽 테이블에서 매치가 되는 레코드들만 리턴함
- 양쪽 테이블의 필드가 모두 채워진 상태로 리턴됨
-
SELECT * FROM raw_data.Vital v JOIN raw_data.Alert a ON v.vitalID = a.vitalID;
2. LEFT JOIN
- 왼쪽 테이블(base)의 모든 레코드를 리턴함
- 오른쪽 테이블의 필드는 왼쪽 레코드와 매칭되는 경우에만 채워진 상태로 리턴됨
SELECT * FROM raw_data.Vital v LEFT JOIN raw_data.Alert a On v.vitalID = a.vitalID;
3. RIGHT JOIN
4. FULL JOIN
- 왼쪽 테이블과 오른쪽 테이블의 모든 레코드들이 리턴
- 매칭되는 경우에만 양쪽 테이블 들의 모든 필드들이 채워진 상태로 리턴됨
SELECT * FROM raw_data.Vital v
FULL JOIN raw_data.Alert a ON v.vitalID = a.vitalID;
5. CROSS JOIN
- 왼쪽 테이블과 오른쪽 테이블의 모든 레코드들의 조합을 리턴
SELECT * FROM raw_data.Vital v CROSS JOIN raw_data.Alert.a;
6. SELF JOIN
- 동일한 테이블을 alias를 달리해서 자기 자신과 조인함
-
SELECT * FROM raw_data.Vital v1 JOIN raw_data.Vital v2 ON v1.vitalID = v2.vitalID
3. 3일차 숙제 REVIEW
- 채널별 월 매출액 테이블 만들기 (본인 스키마 밑에 CTAS로 테이블 만들기)
- session_timestamp, user_session_channel, session_transaction 테이블들 사용
- 아래와 같은 필드로 구성
- month
- channel
- uniqueUsers (총방문 사용자)
- paidUsers (구매 사용자 : refund한 경우도 판매로 포함)
- conversionRate (구매 사용자 / 총방문 사용자)
- grossRevenue (refund 포함)
- netRevenue (refund 제외)
- BOOLEAN 타입 처리
- True or False
- 다음의 2개는 동일한 표현
- flag = True
- flag is True
- 다음의 2개는 동일한 표현인가?
- flag is True
- flag is not False
- (아니다 Null이 있을 수 있어)
-
SELECT COUNT(CASE WHEN flag = True THEN 1 END) true_cnt1, -- return 2 COUNT(CASE WHEN flag is True THEN 1 END) true_cnt2, -- return 2 COUNT(CASE WHEN flag is not False THEN 1 END) not_false_cnt -- return 3 FROM raw_data.boolean_test;
- NULL 비교
- 항상 is 혹은 is not으로 비교해야 함
- = 혹은 != 혹은 <>으로 비교하면 잘못된 결과가 나옴 (주로 return 0)
SELECT COUNT(1)
FROM raw_data.boolean_test
WHERE flag is NULL; -- return 1
SELECT COUNT(1)
FROM raw_data.boolean_test
WHERE flag = NULL; -- return 0
- 복잡한 JOIN 시 먼저 JOIN 전략을 수립하라
- raw_data.user_session_channel
- raw_data.session_timestamp
- raw_data.session_transaction
- 위의 3개 테이블 모두 sessionId를 기반으로 join해야 함
- user_session_channel 과 session_timestamp는 일대일로 join 가능 : INNER JOIN
-session_transaction은 모든 sessionId에 존재하는 것은 아님
- LEFT JOIN (혹은 RIGHT JOIN)
- FROM 에 사용하는 테이블은 user_session_channel 혹은 session_timestamp가 되어야 함
1. 유일한 사용자 수부터 세보기
SELECT LEFT(ts, 7)"month",
user.channel,
COUNT(DISTINCT userid) uniqueUsers
FROM raw_data.user_session_channel usc
JOIN raw_data.session_timestamp t ON t.sessionId = usc.sessionId
GROUP BY 1,2
ORDER BY 1,2;
2. session_transaction 테이블 추가하기
SELECT LEFT(ts, 7)"month",
user.channel,
COUNT(DISTINCT userid) uniqueUsers
FROM raw_data.user_session_channel usc
JOIN raw_data.session_timestamp t ON t.sessionId = usc.sessionId
LEFT JOIN raw_data.session_transcation st ON st.sessionId = usc.sessionId
GROUP BY 1,2
ORDER BY 1,2;
3. paidUsers 추가하기
SELECT LEFT(ts, 7)"month",
user.channel,
COUNT(DISTINCT userid) uniqueUsers,
COUNT(DISTINCT CASE WHEN amount > 0 THEN usc.userid END) paidUsers
FROM raw_data.user_session_channel usc
JOIN raw_data.session_timestamp t ON t.sessionId = usc.sessionId
LEFT JOIN raw_data.session_transaction st ON st.sessionId = usc.sessionId
GROUP BY 1,2
ORDER BY 1,2;
4. conversionRate 추가하기
- paidUsers/uniqueUsers AS conversionRate : 정수/정수 이므로 제대로 계산되지 않는다.
- paidUsers::float/uniqueUsers AS conversionRate : 너무 소숫점이 많이 나온다.
- ROUND(paidUsers*100.0 / uniqueUsers, 2) AS conversionRate : 100.0이 실수이므로 실수로 전환되어 계산됨
- ROUND(paidUsers*100.0 / NULLIF(uniqueUsers, 0), 2) AS conversionRate : 분모가 0일 경우 NULL을 리턴
- NULLIF
- paidUsers/uniqueUsers
- 0으로 나누는 경우 dived by 0 에러 발생
- NULLIF를 사용하여 0을 NULL로 변경하여 방지
- paidUsers/NULLIF(uniqueUsers,0)
- 다시 한번 사칙연산에 NULL이 들어가면 결과도 NULL이 됨을 기억할 것
- paidUsers/uniqueUsers
5. 채널별 월 매출액 테이블 만들기
SELECT LEFT(ts, 7)"month",
channel,
COUNT(DISTINCT usc.userid) uniqueUsers,
COUNT(DISTINCT CASE WHEN amount > 0 THEN usc.userid END) paidUsers,
ROUND(paidUsers*100.0/NULLIF(uniqueUsers,0),2)conversionRate,
SUM(amount)grossRevenue,
SUM(CASE WHEN refunded is False THEN amount END)netRevenue
FROM raw_data.user_session_channel usc
LEFT JOIN raw_data.session_timestamp t ON t.sessionId = usc.sessionId
LEFT JOIN raw_data.session_transaction st ON st.sessionId = usc.sessionId
GROUP BY 1,2
ORDER BY 1,2;
- COALESCE
- NULL값을 다른 값으로 바꿔주는 함수: 즉 NULL 대신 다른 백업값을 리턴해주는 함수
- COALESCE (exp1, exp2, exp3, ...)
- exp1부터 인자를 하나씩 살펴서 NULL이 아닌 값이 나오면 그걸 리턴
- 끝까지 갔는데도 모두 NULL이면 NULL을 리턴
-
SELECT value, COALESCE(value, 0) --value가 NULL이면 0을 리턴 FROM raw_data.count_test;
- 공백 혹은 예약 키워드를 필드 이름으로 사용하려면? ""로 둘러싸서 사용
CREATE TABLE rollingsnowball.test(
group int primary key,
'mailing address'varchar(32)
);
6. 채널별 월매출액 테이블 만들기 (숙제 최종 완성본)
DROP TABLE IF EXISTS adhoc.rollingsnowball_monthly_channel_summary;
CREATE TABLE adhoc.rollingsnowball_monthly_channel_summary AS
SELECT LEFT(ts, 7)"month",
channel,
COUNT(DISTINCT usc.userid) uniqueUsers,
COUNT(DISTINCT CASE WHEN amount > 0 THEN usc.userid END) paidUsers,
ROUND(paidUsers*100.0/NULLIF(uniqueUsers,0),2)conversionRate,
SUM(amount)grossRevenue,
SUM(CASE WHEN refunded is False THEN amount END)netRevenue
FROM raw_data.user_session_channel usc
LEFT JOIN raw_data.session_timestamp t ON t.sessionId = usc.sessionId
LEFT JOIN raw_data.session_transaction st ON st.sessionId = usc.sessionId
GROUP BY 1,2;
--*CTAS 쓸 때 ORDER BY는 무시되므로 쓸 필요 없음
4. 4일차 숙제
1. 사용자별로 처음 채널과 마지막 채널 알아내기
- ROW_NUMBER vs. FIRST_VALUE/LAST_VALUE
- 사용자 251번의 시간 순으로 봤을 때 첫 번째 채널과 마지막 채널은 무엇인가?
-
이렇게 해서 처음 채널과 마지막 채널을 볼 수도 있다SELECT ts, channel FROM raw_data.user_session_channel usc JOIN raw_data.session_timestamp st ON usc.sessionId = st.sessionId WHERE userId = 251 ORDER BY 1
- ROW_NUMBER를 활용해보자 (셀렉트된 레코드들에 특정 기준에 따른 일련번호를 부여하는 것)
- ROW_NUMBER() OVER (PARTITION BY field1 ORDER BY field2)nn
2. Gross Revenue가 가장 큰 UserID 10개 찾기
- user_session_channel과 session_transaction과 session_timestamp테이블을 사용
- Gross Revenue: refund 포함한 매출
3. raw_data.nps 테이블을 바탕으로 월별 NPS 계산
- 고객들에게 다른 사람들에게 추천할 의향이 있는지 물음: 0(의향 없음)에서 10(의향 아주 높음)
- detractor(비추천자): 0~6
- passive(소극자): 7,8
- promoter(홍보자): 9, 10
- NPS = promoter 퍼센트 - detractor 퍼센트
'Data > SQL' 카테고리의 다른 글
MYSQL TABLE 삭제하기 - DROP TABLE (0) | 2021.11.23 |
---|---|
[AI class W11 D5] SQL Analysis 5 (0) | 2021.07.18 |
[AI class W11 D3] SQL Analysis 3 (0) | 2021.07.07 |
[AI class W11 D2] SQL Analysis 2 (0) | 2021.07.07 |
[AI class W11 D1] SQL Analysis 1 (0) | 2021.07.06 |