4. 계산기 탁탁탁

17240 단어 문제문제

돈이 행복을 가져다주지 못한다면, 적어도 안락 속에서 불행할 수 있도록 해줍니다.
- 헬렌 걸리 브라운 (Helen Gurley Brown)


시대가 변하고 사람이 변해도
백화점에 가면 그 특유의 분위기에 압도당한다.
여러가지 제품들이 멋드러지게 진열되어 있는 모습은
예나 지금이나 이쁘게 보인다.

하지만 그 아름다운 진열 뒤에서
일하고 있는 사람들은 어떨까?
항상 앞에서 웃고 있는 사람들도 있지만
뒤에선 땀을 흘리며 일을 하는 사람들이 더 많다.

특히 계산기 두르려가면서 돈 문제를 다루는
사람들은 특히나 숫자 하나하나가 중요할 것이다.
단 한번의 실수라도 했다간 많은 사람들이 골치 아파질 것이다.

예를 들어 다음과 같은 문제들이 있다고 해보자.
(참고로 여기서 나오는 문제들은 SQL 문제입니다.
왜 갑자기 Python에서 SQL로 넘어왔는지는 묻지 마세요...)

1. 쇼핑몰의 일일 매출액

Brazilian E-Commerce Public Dataset by Olist 데이터셋은 브라질의 이커머스 웹사이트인 Olist Store의 판매 데이터 입니다. 그 중 olist_orders_dataset 테이블에는 주문 ID, 고객 ID, 주문 상태, 구매 시각 등 주문 내역 데이터가 들어있습니다. olist_order_payments_dataset 테이블에는 주문 ID, 결제 방법, 결제 금액 등 각 주문의 결제와 관련된 정보가 저장되어 있습니다. 두 테이블을 이용해 2018년 1월 1일 이후 쇼핑몰의 일일 매출액을 계산하는 쿼리를 작성해주세요.

주문 각각에 대해 매출이 일어나는 시점은 olist_orders_dataset 테이블의 order_purchase_timestamp 컬럼에 기록되고, 주문 금액은 olist_order_payments_dataset 테이블의 payment_value 컬럼에 기록됩니다.

쿼리 결과는 아래 두 컬럼을 포함해야 하고, 매출 날짜 기준으로 오름차순 정렬되어 있어야 합니다. 매출액은 반올림 해 소수점 둘째자리까지 출력해주세요.

dt - 매출 날짜 (예: 2018-01-01)
revenue_daily - 해당 날짜의 매출액

그렇다면 우선 우리는 이럴 때 어떻게 해야할까?
우선 문제의 조건들을 하나하나씩 읽고 만족시켜야 한다.
조건들을 살펴보자.

1) 2018년 1월 1일 이후의 기록들만 쓴다.
2) 매출 날짜 기준으로 오름차순으로 정렬한다.
3) 매출액은 반올림 해 소수점 둘째자리까지 출력한다.
4) dt - 매출 날짜 (예: 2018-01-01)
    revenue_daily - 해당 날짜의 매출액

이런 조건들이 있다.

우선 저 조건들을 만족하기에 앞서 저 두개의 테이블을 합치자.
olist_orders_dataset 와 olist_order_payments_dataset 를.
저 두개의 테이블에는 다행히 완전히 같은 컬럼이 있는데
"order_id" 라는 컬럼이다.
그럼 저 order_id를 이용해서 두 테이블을 합쳐보자.

olist_orders_dataset as ood 
join olist_order_payments_dataset as oopd 
on ood.order_id = oopd.order_id

두 개의 테이블을 합치는 방법은 여러가지가 있지만
난 그중에서 이 방법을 선택했다.
테이블의 이름이 너무 긴 관계로
olist_orders_dataset를 ood로 줄이고
olist_order_payments_dataset를 oopd로 줄였다.
그리고 두 테이블의 공통된 칼럼 order_id로
두 테이블을 합친다.

이렇게 되면 두 개의 테이블을 하나로 합쳤다.
(참고로 두 개의 테이블에 중복된 칼럼을 써야 할 경우,
"테이블명.칼럼명" 이런 식으로 써야 한다.
물론 중복이 되지 않았으면 그냥 칼럼명을 써야 한다.)

1. 첫째 조건

그럼 첫째로 매출 날짜 부터 손을 보자.
만약 아무런 조건이 없을 때 테이블을 보면 이렇게 나온다.

select order_purchase_timestamp

이렇듯 날짜와 함께 시간까지 나오게 된다.
하지만 문제는 날짜만 원한다고 한다.
그럼 어떻게 해야할까?
수많은 방법이 있지만 그중에서 나는
substr을 선택했다.

substr(문자열, 시작하는 문자의 순서, 시작한 문자로부터 해당 갯수)
이란 함수는 문자열 중에서 원하는 범위까지만 고르는 함수이다.
(참고로 SQL의 세계에서는 1이 처음이다.
아니 좀 Python은 0이 처음이고 여기는 1이 처음이고
좀 제발 통일 좀 해라 이 프로그래머 놈들아...)

a = "Nevertheless"
substr(a, 1, 5)
"Never"

이런 식으로 나오게 된다.
그렇다면 order_purchase_timestamp들에 있는 숫자들을
년월일 만 나오도록 바꿔보자.

substr(order_purchase_timestamp, 1, 10)

이렇게 하면 모든 order_purchase_timestamp라는
컬럼에 있는 숫자들 모두가 적용이 되서
결과들이 이렇게 된다.
("-"도 문자에 포함된다는 걸 잊으면 안 된다!)

아 그리고 매출날짜를 dt로 바꾸라는 조건이 있었으니깐
바로 적용하자!

substr(order_purchase_timestamp, 1, 10) as dt

자 그럼 진짜 본격적으로 만족을 시켜보자.
2018년 1월 1일 이후라고 했다.

where dt >= '2018-01-01'

"where" 명령은 특정 조건을 만족하는 값들을 걸러내기 위해
쓰이는 명령이다.

이제 첫번째 조건은 완벽히 만족시켰다.

2. 둘째 조건

매출 날짜 기준으로 오름차순으로 정렬을 하려면
방법은 간단하다.

order by dt

"order by"라는 명령을 쓰면 해당 칼럼을 오름차순으로 정렬을 한다.
order by는 자체적으로 오름차순 정렬을 한다.
만약 내림차순 정렬을 하고 싶으면 끝에 desc를 쓰면 된다.

3. 셋째 조건

매출액은 반올림 해 소수점 둘째자리까지 출력하려면
우선 매출액부터 구해야 하는 것이 인지상정.
매출액은 말그대로 주문 금액을 전부 다 합친 것으로
주문 금액들을 모두 합치면 된다.

sum(payment_value)

"sum"이란 명령을 쓰면 해당 칼럼의 값들을 전부 합친다.

이제 다 되었다!
근데 정말로 그럴까?
뭔가 이상하지 않나?
분명 백화점이 하루만 장사를 하는 게 아닐텐데
왜 어째서 2018년 4월 25일 것만 나왔는가?
하루 매출액 치곤 지나치게 많지 않나?
날짜별로 나와야 하지 않나?

그런 의문이 들었다면 옳게 생각한 겁니다!
단순히 sum만 하게되면
특정 날짜 범위에 해당하는
매출액들이 전부다 합쳐진 경우가 되어버린다.
총 매출액이 아닌
일일 매출액을 구하려면
매출액들을 날짜 별로 나누어야 한다.

그렇게 하려면 또다른 명령을 해야 한다.

group by dt

"group by"는 해당 항목을 기준으로 나누는 것이다.
dt는 앞에서 말했던 매출 날짜 이므로
group by dt라는 말은 매출 날짜에 맞춰 나누는 것이다.
그래서 그걸 적용하면 이렇게 된다.


이제 합쳐진 값들을 반올림하면 된다.

round(sum(payment_value), 2)

"round(값, 반올림해서 남길 숫자자리)"라는 명령을 쓰면
해당 자리까지 남기도록 반올림을 해준다.

그리고 이것 역시 해당 날짜의 매출액을 revenue_daily로
바꾸라는 조건이 있으니 적용해보자!

round(sum(payment_value), 2) as revenue_daily


이것으로 모든 것을 다 마쳤다!
이제 이것들을 전부다 하나로 합쳐보자.

select substr(order_purchase_timestamp, 1, 10) as dt,
round(sum(payment_value), 2) as revenue_daily
from olist_orders_dataset as ood 
join olist_order_payments_dataset as oopd 
on ood.order_id = oopd.order_id
where dt >= '2018-01-01'
order by dt;

이렇게 여러분은 복잡한 SQL 문제를
풀게 되었습니다!
박수!
(여러분은 박수를 받아 마땅합니다.)

2. 쇼핑몰의 일일 매출액과 ARPPU

자 그럼 이왕 이렇게 된 거 한발자국 더 나아갑시다!
두번째 문제는 첫번째 문제와 똑같습니다.
다만 몇가지가 더 추가되었습니다.

Brazilian E-Commerce Public Dataset by Olist 데이터셋은 브라질의 이커머스 웹사이트인 Olist Store의 판매 데이터 입니다. 그 중 olist_orders_dataset 테이블에는 주문 ID, 고객 ID, 주문 상태, 구매 시각 등 주문 내역 데이터가 들어있습니다. olist_order_payments_dataset 테이블에는 주문 ID, 결제 방법, 결제 금액 등 각 주문의 결제와 관련된 정보가 저장되어 있습니다. 두 테이블을 이용해 2018년 1월 1일 이후 일별로 집계된 쇼핑몰의 결제 고객 수, 매출액, ARPPU를 계산하는 쿼리를 작성해주세요.


ARPPU는 Average Revenue Per Paying User의 약자로, 결제 고객 1인 당 평균 결제 금액을 의미합니다. 전체 매출액을 결제 고객 수로 나누면 ARPPU를 계산할 수 있습니다.


주문 각각에 대해 매출이 일어나는 시점은 olist_orders_dataset 테이블의 order_purchase_timestamp 컬럼에 기록되고, 주문 금액은 olist_order_payments_dataset 테이블의 payment_value 컬럼에 기록됩니다.


쿼리 결과는 아래 네 개의 컬럼을 포함해야 하고, 매출 날짜 기준으로 오름차순 정렬되어 있어야 합니다. 매출액과 ARPPU는 반올림 해 소수점 둘째자리까지 출력해주세요.


dt - 매출 날짜 (예: 2018-01-01)
pu - 결제 고객 수
revenue_daily - 해당 날짜의 매출액
arppu - 결제 고객 1인 당 평균 결제 금액

제가 말했죠? 완전 똑같다고요.
몇가지 추가된 거 만 빼면요!

그럼 추가된 두가지 항목을 차례로 구해봅시다.

1. 첫째 항목

결재 고객 수를 구해봅시다.
customer_id가 딱 봐도 고객의 이름을 가지고 있을 거라고 감이 오죠?
그럼 모든 customer_id를 구해봅시다.

count(customer_id)

count() 함수는 말그대로 해당 칼럼의 갯수를 구하는 것입니다.
만약 아무런 조건 없이 count()를 썼으면 칼럼에 해당되는 모든 값들이
전부다 합쳐졌겠죠?

하지만 앞에 이미 group by 명령을 쓴 상태여서
dt, 즉 매출 날짜에 나누어져서 고객들이 모아졌을 겁니다.

자 그럼 이게 끝입니다!
근데 정말 그럴까요?
혹시 이런 경우가 있지 않을까요?
하루에 2번 이상 따로따로 결제를 하는 고객들이 있지 않을까요?
그런 경우는 충분히 많이 일어납니다.

그렇다면 이 문제는 어떻게 해결해야 할까요?
그러기 위해 우리는 추가적인 명령문을 쓰면 됩니다.

count(distinct customer_id)

"distinct"라는 명령을 쓰면 중복이 되는 고객들을 지울 수 있습니다.
그러면 정말로 개개인의 수를 구할 수 있게 됩니다.

아 그리고 결제 고객의 수를 pu로 바꾸라고 했으니
한번 적용해봅시다.

count(distinct customer_id) as pu

이렇게 결제 고객의 수는 구해졌습니다.

2. 둘째 항목

자 그럼 이제 결제 고객 1인 당 평균 결제 금액을 구해봅시다.
어려워 보이지만 사실 궂은 일들은
앞에서 이미 다해가지고 걱정을 할 필요 없습니다!

우선 하루의 총 매출액이 있을 것이고
하루의 총 결제 고객수가 있을 것이다.
그렇다면 하루의 고객 1인당 평균 결제 금액을 구하려면
그냥 총 매출액을 총 결제 고객수와 나누면 끝이다.

(sum(payment_value)/count(distinct customer_id)


여기서 더 나아가
반올림해서 소수 둘째자리까지 출력을 하라고 했으니
round 함수를 써야 하겠죠?

round((sum(payment_value)/count(distinct customer_id)), 2)

그리고 결제 고객 1인 당 평균 결제 금액을 arppu로 바꾸라 했으니
그것도 적용해 봅시다.

round((sum(payment_value)/count(distinct customer_id)), 2) as arppu

이제 진심으로 다 끝났습니다!
이제 이 모든 걸 한자리에 다 모으면 이렇게 됩니다.

select substr(order_purchase_timestamp, 1, 10) as dt, 
  count(distinct customer_id) as pu, 
  round(sum(payment_value), 2) as revenue_daily, 
  round((sum(payment_value)/count(distinct customer_id)), 2) as arppu
from olist_orders_dataset as ood 
join olist_order_payments_dataset as oopd 
on ood.order_id = oopd.order_id
where dt >= '2018-01-01'
group by dt
order by dt;

여러분 정말 고생많으셨습니다.
특히 한평생을 코딩의 코자도 모르는 사람들은
더욱더 고생했습니다.
나중에 시간 있을 때 꼭 맛있는 음식 드세요.
다이어트 그런거 생각치 마시고 그냥 드세요.
여러분은 마땅히 그래도 됩니다.

오늘 하루 지지리도 긴 문제풀이 봐주셔서 너무 감사합니다.
다음 이 시간에 또 만나요!

좋은 웹페이지 즐겨찾기