엑셀보다 쉽고 빠른 SQL

엑셀보다 쉽고 빠른 SQL 4주차

김호진02 2024. 10. 16. 17:11

 

1. 여러번의 연산을 한 번의 SQL문으로 수행하기 (SubQuery)

2. User Segmentation와 조건별 수수료를 Subquery로 결합해보기

3. 복잡한 연산을 Subquery로 수행하기

4.필요한 데이터가 서로 다른 테이블에 있을 때 (JOIN)

5. [실습] JOIN으로 두 테이블의 데이터 조회하기

6. [실습] JOIN으로 두 테이블의 값을 연산하기

7. 숙제 및 후기


1. 여러번의 연산을 한 번의 SQL문으로 수행하기 (SubQuery)

Subquery가 필요한 경우

 여러번의 연산을 수행해야 할 때

 조건문에 연산 결과를 사용해야 할 때

 조건에 Query결과를 사용하고 싶을 때

 

서브퀴리 예시

1) FROM문에서 

select column1, special_column

from

( /* subquery */

select column1, column2 special_column

from table1

) a

 

2) WHERE 문에서

select column1, column2

from table1

where column1 = (select col1 from table2)

 

- 음식 단가 조회 하기

(2) SELECT price/quantity

FROM

(

(1) SELECT price, quantity

FROM food_orders

) a;

(1) 에서 가격과 음식 수량을 먼저 조회 후 

조회된 결과 값을 이용하여 (2) 에서 연산을 수행한다.

음식 단가 조회하기

 

select order_id, restaurant_name, if(over_time>=0, over_time, 0) over_time

from

(

select order_id, restaurant_name, food_preparation_time-25 over_time

from food_orders

) a

음식 준비시간 오버타임 조회

 

음식 준비시간-25를 먼저 한 후 오버 타임을 구할 수 있다. 25 분보다 준비시간이 적을경우 if문을 사용하여 0분으로!

 


2. User Segmentation와 조건별 수수료를 Subquery로 결합해보기

  • 음식점의 평균 단가별 segmentation 을 진행하고, 그룹에 따라 수수료 연산하기 
  • ~5000원 미만 0.05% ~20000원 미만 1% ~30000원 미만 2% 30000원 초과 3%)

select restaurant_name,

price_per_plate*ratio_of_add "수수료"

from

(

(2) select restaurant_name,

case when price_per_plate<5000 then 0.005

when price_per_plate between 5000 and 19999 then 0.01

when price_per_plate between 20000 and 29999 then 0.02

else 0.03 end ratio_of_add,

price_per_plate  -- (1)결과에 따라 수수료 계산

from

(

(1) select restaurant_name, avg(price/quantity) price_per_plate

from food_orders

group by 1 -- 식당별 단가 평균

) a

) b

1) 음식점별 평균 단가를 조회

2) (1)에서 조회 된 값(평균 음식단가)을 (2)에서 조건문을 사용하여 수수료를 계산

3) 음식점별 평균음식단가*수수료를 계산하여 조회 

 

  • 음식점의 지역과 평균 배달시간으로 segmentation 하기

(2) SELECT restaurant_name, sido,avg_delivery_time,

CASE WHEN avg_delivery_time<=20 THEN "<=20"

WHEN avg_delivery_time>20 and avg_delivery_time<=30 THEN '20<x<=30'

ELSE '>30' END delivery_time_segment

FROM

(

(1) SELECT restaurant_name, SUBSTR(addr,1,2) sido,

AVG(delivery_time) avg_delivery_time

FROM food_orders fo

GROUP BY 1,2

) a

1) 음식점,지역별 평균배달시간과, 주소를 조회

2) (1)에서 조회된 평균 배달시간을 이용하여 segmentation

 


3. 복잡한 연산을 Subquery로 수행하기

1)음식 타입별 총 주문수량과 음식점 수를 연산하고, 주문수량과 음식점수 별 수수료율을 산정하기

  • (음식점수 5개 이상, 주문수 30개 이상 → 수수료 0.5%
  • 음식점수 5개 이상, 주문수 30개 미만 → 수수료 0.8%
  • 음식점수 5개 미만, 주문수 30개 이상 → 수수료 1%
  • 음식점수 5개 미만, 주문수 30개 미만 → 수수로 2%)

SELECT cuisine_type,total_quantity,count_res,

CASE WHEN count_res>=5 AND total_quantity>=30 THEN 0.005

WHEN count_res>=5 AND total_quantity<30 THEN 0.008

WHEN count_res<5 AND total_quantity>=30 THEN 0.01

WHEN count_res<5 AND total_quantity<30 THEN 0.02 END rate

FROM

(

SELECT cuisine_type,SUM(quantity) total_quantity,

count(DISTINCT restaurant_name) count_res

FROM food_orders

GROUP BY 1/*음식 타입별 총 음식 수량과, 음식점 수(중복제거)*/

) a

 

2)음식점의 총 주문수량과 주문 금액을 연산하고, 주문 수량을 기반으로 수수료 할인율 구하기

  • (할인조건
  • 수량이 5개 이하 → 10%
  • 수량이 15개 초과, 총 주문금액이 300000 이상 → 0.5%
  • 이 외에는 일괄 1%)

SELECT restaurant_name, sum_price,sum_quantity,

CASE WHEN sum_quantity<=5 THEN 0.1

WHEN sum_quantity>15 AND sum_price>=300000 THEN 0.005

ELSE 0.01 END discount_rate

FROM

(

SELECT restaurant_name ,SUM(price) sum_price,SUM(quantity) sum_quantity

FROM food_orders

GROUP BY 1

) a;

 

 

-------------------10/16 4-4

 


4.필요한 데이터가 서로 다른 테이블에 있을 때 (JOIN)

두 개의 서로 다른 테이블에서 원하는 값을 가져와 하나의 테이블로 조회를 하고싶을 때 JOIN 을 사용

 



LEFT JOIN: A테이블에 모든 정보와 B테이블에도 있는 정보를 합침.

INNER JOIN : A테이블과 B테이블에 같은 정보를 가져옴.

 

food_orders 테이블과 payments테이블

 

SELECT *

FROM food_orders LEFT JOIN payments ON food_orders.order_id=payments.order_id;

SELECT *

FROM food_orders INNER JOIN payments ON food_orders.order_id=payments.order_id;

 

 

주문 테이블과 고객 테이블을 cusomer_id 를 기준으로 left join 으로 묶어보기 (조회 컬럼 : order_id, customer_id, restaurant_name, price, name, age, gender)

SELECT fo.order_id ,fo.customer_id , fo.restaurant_name ,fo.price ,c.name ,c.gender ,c.age

FROM food_orders fo LEFT JOIN customers c ON fo.customer_id =c.customer_id;


 

5. [실습] JOIN으로 두 테이블의 데이터 조회하기

1) 한국 음식의 주문별 결제 수단과 수수료율을 조회하기

  • (조회 컬럼 : 주문 번호, 식당 이름, 주문 가격, 결제 수단, 수수료율) *결제 정보가 없는 경우도 포함하여 조회

SELECT fo.order_id , fo.restaurant_name , fo.price ,p.pay_type ,p.vat

FROM food_orders fo LEFT JOIN payments p ON fo.order_id=p.order_id

WHERE cuisine_type ='Korean';

 

 

2) 고객의 주문 식당 조회하기

  • (조회 컬럼 : 고객 이름, 연령, 성별, 주문 식당) *고객명으로 정렬, 중복 없도록 조회

SELECT DISTINCT c.name, c.age, c.gender, fo.restaurant_name

FROM food_orders fo LEFT JOIN customers c ON fo.customer_id=c.customer_id

ORDER BY c.name;

 


6. [실습] JOIN으로 두 테이블의 값을 연산하기

1)주문 가격과 수수료율을 곱하여 주문별 수수료 구하기

  • (조회 컬럼 : 주문 번호, 식당 이름, 주문 가격, 수수료율, 수수료) *수수료율이 있는 경우만 조회

SELECT fo.order_id, fo.restaurant_name, fo.price,p.vat,fo.price*p.vat '수수료'

FROM food_orders fo INNER JOIN payments p ON fo.order_id=p.order_id

 

 

 

2) 50세 이상 고객의 연령에 따라 경로 할인율을 적용하고, 음식 타입별로 원래 가격과 할인 적용 가격 합을 구하기

  • (조회 컬럼 : 음식 타입, 원래 가격, 할인 적용 가격, 할인 가격) 할인 : 나이-500.005
    • 고객 정보가 없는 경우도 포함하여 조회, 할인 금액이 큰 순서대로 정렬

SELECT cuisine_type,SUM(price) price,

sum(price*discount_rate) discounted_price

FROM

(

SELECT fo.cuisine_type, fo.price,c.age,(c.age-50)*0.005 discount_rate

FROM food_orders fo LEFT JOIN customers c ON fo.customer_id=c.customer_id

WHERE c.age>=50

) a

GROUP BY 1

ORDER BY discounted_price DESC;

 


7. 숙제 및 후기

1. 식당별 평균 음식 주문 금액과 주문자의 평균 연령을 기반으로 Segmentation 하기

  • 평균 음식 주문 금액 기준 : 5,000 / 10,000 / 30,000 / 30,000 초과
  • 평균 연령 : ~ 20대 / 30대 / 40대 / 50대 이상
  • 두 테이블 모두에 데이터가 있는 경우만 조회(INNER JOIN) , 식당 이름 순으로 오름차순 정렬

5,000 (price_group1) / 10,000(price_group2).....

~20(age_group1) / 30 (age_group2)....

 

먼저 식당별 평균 음식가격과 고객의 평균 나이를 조회(customers_id를 이용하여 두 테이블 join)

그 후, 조회된 결과를 이용하여 평균가격과 평균나이를 Segmentation

SELECT restaurant_name,

CASE WHEN price<=5000 THEN 'price_group1'

WHEN price<=10000 THEN 'price_group2'

WHEN price<=30000 THEN 'price_group3'

ELSE 'price_group4'END AS 'price_group',

CASE WHEN age<30 THEN 'age_group1'

WHEN age<40 THEN 'age_group2'

WHEN age<50 THEN 'age_group3'

ELSE 'age_group4' END AS 'age_group'

FROM

(

SELECT fo.restaurant_name,AVG(fo.price) price ,AVG(c.age) age

FROM food_orders fo INNER JOIN customers c ON fo.customer_id=c.customer_id

GROUP BY 1

) a

ORDER BY 1;

 

 

후기:

SQL문이 점점 길어져 복잡해보이고 읽기도 싫었지만 한줄한줄 천천히 해석하면 쉽게 이해할 수있다!

서브퀴리에 이어 조인까지 하니 이제 모르는게 없는 것 같았지만 초반에는 문제 푸는데 해설이 필요했다..

하지만 여러번 문제를 보다보니 문제만으로 SQL문을 작성할 수 있게 되었따!(나좀 쩌는듯)