4Ls 회고는 뭘까?
Liked : 좋았던 점
Lacked : 아쉬웠던 점
Learned : 배운 점
Longed for : 앞으로 바라는 점
윈도우 함수의 활용
집계 함수
sum(), avg(), count() 등
<예시>
사용 데이터 베이스 : US E-Commerce Records 2020
테이블 : records
Q. 서브 카테고리 별 매출액(sales)을 계산하고 그 매출액이 각 서브 카테고리가 속해있는 카테고리 안에서 비중을 얼마나 차지하는지, 그리고 전체 매출액에서 비중을 얼마나 차지하는지 각각 계산하시오. 결과물은 아래 모양을 참고.
•
category : 카테고리 이름
•
sub_category : 서브 카테고리 이름
•
sales_sub_category : 서브 카테고리 별 매출액 합계
•
sales_category : 카테고리별 매출액 합계
•
sales_total : 전체 매출액
•
pct_in_category : 카테고리 매출 중 해당 서브 카테고리의 매출 비율
•
pct_in_total : 전체 매출 중 해당 서브 카테고리의 매출 비율
select category
,sub_category
,round(sum_sales, 2) as sales_sub_category
,round(sum(sum_sales) over (partition by category), 2) as sales_category -- 카테고리별 매출액 합계
,round(sum(sum_sales) over() , 2) as sales_total -- 전 매출액 합계
,round(sum_sales / sum(sum_sales) over (partition by category), 2) as pct_in_category
,round(sum_sales / sum(sum_sales) over(), 2) as pct_in_total
from (
select category
,sub_category
,sum(sales) as sum_sales -- 서브 카테고리별 매출액 합계
from records
group by category,sub_category
) as sales;
SQL
복사
category | sub_category | sales_sub_category | sales_category | sales_total | pct_in_category | pct_in_total |
Furniture | Chairs | 95554.35 | 215387.27 | 733215.26 | 0.44 | 0.13 |
Furniture | Furnishings | 28915.09 | 215387.27 | 733215.26 | 0.13 | 0.04 |
Furniture | Tables | 60893.54 | 215387.27 | 733215.26 | 0.28 | 0.08 |
Furniture | Bookcases | 30024.28 | 215387.27 | 733215.26 | 0.14 | 0.04 |
Office Supplies | Binders | 72788.05 | 246097.18 | 733215.26 | 0.3 | 0.1 |
Office Supplies | Storage | 69677.62 | 246097.18 | 733215.26 | 0.28 | 0.1 |
Office Supplies | Art | 8863.07 | 246097.18 | 733215.26 | 0.04 | 0.01 |
Office Supplies | Appliances | 42926.93 | 246097.18 | 733215.26 | 0.17 | 0.06 |
Office Supplies | Labels | 3861.22 | 246097.18 | 733215.26 | 0.02 | 0.01 |
Office Supplies | Paper | 27694.72 | 246097.18 | 733215.26 | 0.11 | 0.04 |
Office Supplies | Supplies | 16049.41 | 246097.18 | 733215.26 | 0.07 | 0.02 |
Office Supplies | Fasteners | 857.59 | 246097.18 | 733215.26 | 0 | 0 |
Office Supplies | Envelopes | 3378.57 | 246097.18 | 733215.26 | 0.01 | 0 |
Technology | Copiers | 62899.39 | 271730.81 | 733215.26 | 0.23 | 0.09 |
Technology | Phones | 105340.52 | 271730.81 | 733215.26 | 0.39 | 0.14 |
Technology | Accessories | 59946.23 | 271730.81 | 733215.26 | 0.22 | 0.08 |
Technology | Machines | 43544.68 | 271730.81 | 733215.26 | 0.16 | 0.06 |
RANK
row_number(), rank(), dense_rank()
→ 비즈니스에서
“성연령별로 인기있는 컨텐츠 10개씩 뽑아주세요. 큰 차이가 있나요? 성연령별로 별 차이가 없다고 하면, 어떤 기준으로 나누면 좀 차이가 있을까요? 추천 컨텐츠 영역을 기획하려고 하는데 기준을 어떻게 잡으면 좋을지 고민입니다”
< 예시 >
리트코드 185번
LEAD, LAG
LEAD(), LAG()
→ 비즈니스에서 : 세션 정의하기 등 연속된 로그 안에서 패턴 찾기
“택배 차량이 배송 중간에 정차하는 것을 최소화하고 싶은데, 일단 그 전에 지금 하루 배송 중에 몇 번 정차하는지 한 번 정차 할 때마다 평균적으로 몇 박스 정도 배송하는지 알고싶습니다.”
“사람들이 한 세션 안에 평균적으로 몇 번 클릭을 하는지, 특정 기능을 몇 번 사용하는지 알고 싶습니다. 그런데 세션 id는 db에 없습니다..”
유저 8번의 session 정의하기
step 1. 유저 8번의 행동 로그 추출
select user_id
,event_type
,event_name
,occurred_at
,lag(occurred_at, 1) over(partition by user_id order by occurred_at) as last_event
,lead(occurred_at, 1) over(partition by user_id order by occurred_at) as next_event
,row_number() over () as id
from tutorial.yammer_events e
where e.event_type = 'engagement'
and user_id = 8
order by user_id, occurred_at;
SQL
복사
step 2. 로그들 간 시간 간격 계산
select user_id
,event_type
,event_name
,occurred_at
,occurred_at - lag(occurred_at, 1) over(partition by user_id order by occurred_at) as last_event_term
,lead(occurred_at, 1) over(partition by user_id order by occurred_at) - occurred_at as next_event_term
,row_number() over () as id
from tutorial.yammer_events e
where e.event_type = 'engagement'
and user_id = 8
order by user_id, occurred_at
SQL
복사
step 3. 세션 시작 지점과 끝 지점 로그만 필터링하고, 같은 세션끼리 같은 id 부여
•
last_event 가 없음 , last_event 가 10분 이상임
•
next_event 가 10분 이상임, next_event 가 없음
select bounds.*
,CASE WHEN last_event_term >= interval '10 MINUTE' THEN id
WHEN last_event IS NULL THEN id-- 시작 세션이면 같은 id 부여
ELSE LAG(id,1) OVER (PARTITION BY user_id ORDER BY id) -- 끝 세션이면 그 전의 id를 받아옴
END as session
from(
select user_id
,event_type
,event_name
,occurred_at
,timestampdiff(second, lag(occurred_at) over(partition by user_id order by occurred_at), occurred_at) as last_event_term
,timestampdiff(second, occurred_at, lead(occurred_at, 1) over(partition by user_id order by occurred_at)) as next_event_term
,row_number() over () as id
from tutorial.yammer_events e
where e.event_type = 'engagement'
and user_id = 8
order by user_id, occurred_at
) bounds
where last_event_term >= interval '10 MINUTE'
or last_event_term IS NULL
or next_event_term >= interval '10 MINUTE'
or next_event_term IS NULL
SQL
복사
step 4. 세션 아이디별로 session_start, session_end 값을 가질 수 있도록 데이터 가공
select user_id
,session
,MIN(occurred_at) as session_start -- 세션의 시작
,MAX(occurred_at) as session_end -- 세션의 끝
from(
select bounds.*
,CASE WHEN last_event >= interval '10 MINUTE' THEN id
WHEN last_event IS NULL THEN id-- 시작 세션이면 같은 id 부여
ELSE LAG(id,1) OVER (PARTITION BY user_id ORDER BY id) -- 끝 세션이면 그 전의 id를 받아옴
END as session
from(
select user_id
,event_type
,event_name
,occurred_at
,occurred_at - lag(occurred_at, 1) over(partition by user_id order by occurred_at) as last_event_term
,lead(occurred_at, 1) over(partition by user_id order by occurred_at) - occurred_at as next_event_term
,row_number() over () as id
from tutorial.yammer_events e
where e.event_type = 'engagement'
and user_id = 8
order by user_id, occurred_at
) bounds
where last_event >= interval '10 MINUTE'
or last_event IS NULL
or next_event >= interval '10 MINUTE'
or next_event IS NULL
) final
group by 1,2
SQL
복사
길고 복잡한 쿼리 핵심 로직 이해하기 꿀팁
1.
일부 데이터만 본다. 한 번에 모든 데이터를 다 보면 핵심 로직 보기전에 헷갈려서 포기하게 된다. (user_id = 8)
2.
쿼리를 분해하고, 로직의 가장 안쪽에 있는 서브쿼리부터 이해한다.
3.
쿼리 결과를 스프레드 시트로 정리하며 스텝 별 데이터와 쿼리의 논리를 눈으로 확인한다.
4.
포기하지 말고 정확하고 명확하게 이해될 때까지 끈질기게 본다.