카테고리 없음

[ 유데미 스타터스 ] 52일차 TIL 프로젝트 기반 SQL 실전 트레이닝 - 미니 프로젝트 1

하지수지수 2023. 4. 23. 22:45

1. 개요

1.1 데이터셋 설치

① 스키마 생성

② olist_ec.sql 파일 불러오기

③ 테이블 생성

④ psql 실행

    → client_encoding 변경

    → 데이터 가져오기

⑤ pk 생성, fk 생성

 

Brazilian E-Commerce Public Dataset by Olist

100,000 Orders with product, customer and reviews info

www.kaggle.com

 

1.2 테이블 구조 파악

- product_id가 같은 제품을 여러 sellers가 판매할 수 있음

- sellers가 product가 아니라 order_items와 연관이 있음

- product_name_length가 비식별화되어 있기 때문에 제품명은 알 수 없고 카테고리 이름만 알 수 있음

- product_category_name_translation으로 카테고리 이름을 영어로 확인 가능함

- orders와 customers의 customer_id는 1:1 관계로 고객을 유일하게 식별하는 식별자는 customer_unique_id임

 

1.3 SQL 데이터 분석

① 데이터 탐색

② 분석 목적 설정

③ 지표 설정 및 분석 계획

④ 데이터 추출/정제/가공/분석

⑤ 리포트 작성 및 발표

 

✔ 분석 목적 설정

- 고객 담당자

EX) 효과적인 고객관리 전략 수립을 위한 데이터 분석

- 유통 담당자

- 제품 담당자

- 배송 담당자

➡️ 비즈니스 가치를 창출하기 위해 어떤 지표를 추출해서 분석하면 좋을지 고민하기

 


2. 미니 프로젝트 기획

2.1 분석 상황 설정

1) 우리는?

- Olist 입점을 원하는 seller

 

2) 우리의 목적

- 매출 확보를 위한 판매 전략을 세우기 위한 데이터 분석

 

3) 분석 과정

① 어떤 고객을 대상으로 해야 할까?

- RFM 고객 분석

② 어떤 제품을 판매해야 할까?

- 매출 TOP 10 카테고리

- 꾸준히 판매되고 있는 카테고리(재구매율)

③ 어떤 지역에서 판매해야 할까?

- 매출 TOP 10 카테고리가 판매되는 지역

- 배송기간과 주문건수의 상관관계

 

2.2 현황 파악 분석

📌 고객 구매지표 분석

필요한 컬럼

- customers : customer_id, customer_unique_id, customer_city

- orders : order_id, order_status

- order_payments : payment_value

- order_items : order_item_id, product_id, seller_id, price

- products : product_category_name

- product_category_name_translation : product_category_name_english

with cte_customers as (
select c.customer_id, c.customer_unique_id, c.customer_city
	, o.order_id, o.order_status
	, oi.order_item_id, oi.product_id, oi.seller_id, oi.price
	, p.product_category_name
	, pcnt.product_category_name_english 
from customers c, orders o, order_items oi, products p, product_category_name_translation pcnt
where c.customer_id = o.customer_id 
	and o.order_id = oi.order_id 
	and oi.product_id = p.product_id 
	and p.product_category_name = pcnt.product_category_name
)

 

도시 별 매출액, 주문건수, 고객수 집계 / 구성비

-- 도시별 매출액, 주문건수, 고객수 집계
, cte_city as (
select customer_city
	, count(distinct customer_unique_id) as 고객수
	, sum(price) as 매출액
	, count(distinct order_id) as 주문건수
from cte_customers
group by 1
order by 3 desc
)
-- 구성비
select customer_city
	, 고객수
	, round((고객수/sum(고객수) over())*100,2)::varchar(10)||'%' as 고객수구성비
	, 매출액
	, round((매출액/sum(매출액) over())::numeric*100,2)::varchar(10)||'%' as 매출액구성비
	, 주문건수
	, round((주문건수/sum(주문건수) over())*100,2)::varchar(10)||'%' as 주문건수구성비
from cte_city;

 

✅ 매출액 TOP 3 도시 별 판매된 제품 순위

- 정렬은 매출액을 기준으로, 괄호 안은 판매 수량을 표시

-- 도시별 카테고리 매출액
, cte_city_product_sale as (
select customer_city, product_category_name_english, sum(price) as 매출액, count(product_id) as 판매수량
from cte_customers
group by 1,2
order by 4 desc
)
-- 도시별 카테고리 매출액 순위
, cte_city_product_sale_rank as (
select customer_city, '['||product_category_name_english||'] '||'('||판매수량||')' as product_category_name_english
	, row_number() over(partition by customer_city order by 매출액 desc) as 순위
from cte_city_product_sale
)
-- 도시별 매출액순위 비교
-- 매출액 top3 (sao paulo, rio de janeiro, belo horizonte)
, cte_city_group_product_sale_rank_pivot as (
 select 순위
 	, max(case when customer_city='sao paulo' then product_category_name_english end) as "sao paulo"
 	, max(case when customer_city='rio de janeiro' then product_category_name_english end) as "rio de janeiro"
 	, max(case when customer_city='belo horizonte' then product_category_name_english end) as "belo horizonte"
 from cte_city_product_sale_rank
 group by 순위
 order by 순위
 limit 10
 )
select * from cte_city_group_product_sale_rank_pivot;

 

✅ 고객 별 매출액, 주문건수, 건당 평균 주문액

-- 고객별 매출액, 주문건수, 건당 평균주문
, cte_customer_info as (
select customer_unique_id
	, count(distinct order_id) as 주문건수
	, sum(price) as 매출액
	, sum(price)/count(distinct order_id) as 건당평균주문
from cte_customers
group by 1
order by 3 desc
)
select * from cte_customer_info;

 


3. 지표 설정 및 분석

3.1 어떤 고객을 대상으로 해야 할까?

 RFM 지표 설정

- Order Status : delivered

- 기준일 : order_purchase_timestamp

 

1) RFM 집계

-- 1) max order_date
, cte_customer_maxo as (
select *
	 , MAX(order_date) over() as maxo
from cte_main
)
-- 2) 고객별 RFM
, cte_rfm as (
select customer_unique_id
	 , max(maxo) - max(order_date) as Recency
	 , count(distinct order_id) as Frequency
	 , sum(price) as Monetary
from cte_customer_maxo
group by 1
)

 

2) 단계 정의 및 RFM 점수 부여

- Recency 5등분 후 1~5점

- Frequency 1=1, 2=2, 3/4=3, 4이상=4

- Monetary 5등분 후 1~5점

-- 3) 1차 rfm_score
, cte_rfm_score as (
select customer_unique_id
	 , recency
	 , ntile(10) over (order by recency desc) as r
	 , frequency
	 , case 
	 	when frequency < 2 then 1
	 	when frequency = 2 then 2
	 	when frequency <= 4 then 3
	 	else 4
	 end as f
	 , monetary
	 , ntile(10) over (order by monetary asc) as m
from cte_rfm
order by r desc, f desc, m desc
)
-- 4) 2차 rfm_score
, cte_rfm_final as (
select customer_unique_id
	 , recency as r
	 , case 
		 when r <= 2 then 1
		 when r <= 4 then 2
		 when r <= 6 then 3
		 when r <= 8 then 4
		 when r <= 10 then 5
	 	end recency
	 , frequency as f
	 , f as frequency 
	 , monetary as m
	 , case 
		 when m <= 2 then 1
		 when m <= 4 then 2
		 when m <= 6 then 3
		 when m <= 8 then 4
		 when m <= 10 then 5
	 	end monetary
from cte_rfm_score
)

 

✅ RFM Score 별 주요 지표

 

1) RFM Score 별 고객 수

-- 5) customer 별 r,f,m과 rfm_score
, rfm_customer_status as (
select *, recency + frequency + monetary as rfm_score
from cte_rfm_final
order by rfm_score desc, r desc, f desc, m desc
)
-- 6) rfm_score 별 r, f, m 별 고객 수 
, cte_final1 as (
select rfm_score
	 , recency
	 , frequency
	 , monetary
	 , count(customer_unique_id) as customer_cnt
from rfm_customer_status
group by 1,2,3,4
order by 2 desc, 3 desc,4 desc
)
-- 7) rfm_score 별 고객 수
, cte_final2 as (
select rfm_score
	 , count(customer_unique_id) as customer_cnt
from rfm_customer_status
group by 1
order by 1 desc
)
select * from cte_final2;

 

2) RFM Score 별 주요 지표 집계

-- 매출 구하기 위해 필요한 컬럼(Monetary=매출) 가져와서 고객 등급표 INNER JOIN
, cte_customer_status_amount as (
select rcs.customer_unique_id
	 , cr.Recency
	 , rcs.frequency as f
	 , rcs.r as r
	 , cr.frequency
	 , cr.Monetary
	 , rcs.rfm_score
from rfm_customer_status as rcs
	 inner join cte_rfm as cr on rcs.customer_unique_id = cr.customer_unique_id
order by 5
)
-- rfm_score 별 주요 지표 집계
, cte_customer_status_amount2 as (
select rfm_score
	 , round(sum(Monetary)) as 총구매액
	 , count(customer_unique_id) as 고객수
	 , round(sum(Monetary) / count(customer_unique_id)) as 평균구매액
	 , round(avg(f),1)||'번' as 평균주문수
	 , round(avg(to_char(r,'dd')::int),1)||'일' as 평균최근구매일
from cte_customer_status_amount
group by rfm_score
order by 1 desc
)

 

✅ 타겟 고객 선정

✔ 14점 ~ 8점까지의 Score그룹을 타겟 고객으로 선정

- r-f-m 중 한 수치에 값이 극단적으로 몰리는 것을 고려함

- 최근구매일/구매액의 분포 폭을 고려하여 전체 고객 수에서 차지하는 비중과 총 매출액에서 차지하는 비중이 어느 정도 확보되는 표본을 구성하고자 함

-- 매출 구성비
, cte_rfm_sales as (
select z1.customer_unique_id, avg(rfm_score) as rfm_score, sum(price) as sales
from rfm_customer_status z1, customers c2, orders o2 , order_items oi2
where z1.customer_unique_id = c2.customer_unique_id
  and c2.customer_id = o2.customer_id
  and o2.order_id = oi2.order_id
group by z1.customer_unique_id
order by 2 desc, 1
)
, cte_rfm_total_sales as (
select rfm_score
, sum(sales) as rfm_total_sales
from cte_rfm_sales
group by rfm_score
)
, cte_sales_ratio as (
select *
, sum(rfm_total_sales) over() as total_sales
, rfm_total_sales / sum(rfm_total_sales) over() * 100 as ratio
from cte_rfm_total_sales
)
, cte_ratio_agg as (
select *, sum(ratio) over (order by rfm_score desc) as ratio_agg
from cte_sales_ratio
)
-- 고객 선별
, cte_select_score as (
select *
from cte_ratio_agg
where ratio_agg <= 80
)
select * from cte_select_score;

→ 타겟 그룹은 전체 고객 수 중 40.77%의 고객 수를 차지하고, 전체 매출 중 64.57%를 차지함