์ŠคํŒŒ๋ฅดํƒ€ ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„(25.12.01~)

์ŠคํŒŒ๋ฅดํƒ€ ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„_๋ณธ์บ ํ”„_data11๊ธฐ ๊น€์„ ์˜_TIL_Day 4

0๏ธโƒฃ 2025. 12. 26. 22:34

<์˜ค๋Š˜์˜ ํ•™์Šต>

  • 10์‹œ: SQL ๋ผ์ด๋ธŒ ์„ธ์…˜ 3ํšŒ์ฐจ
  • 14์‹œ: ์ง๋ฌด๋ฐœํ‘œ
  • SQL ๋ฐ์ผ๋ฆฌ ํ€˜์ŠคํŠธ & ํ€ด์ฆˆ

<ํ•™์Šต ๋‚ด์šฉ ์ •๋ฆฌ>

1. SQL ๋ผ์ด๋ธŒ ์„ธ์…˜ 3ํšŒ์ฐจ

  • ์ฒ˜์Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณผ ๋•Œ PK(๊ธฐ๋ณธํ‚ค)๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ ์œ ํ•œ ๊ฐ’์ด ์žˆ๋Š” col์„ ํŒŒ์•…ํ•˜๋Š” ์Šต๊ด€!
  • group by + ์ง‘๊ณ„ํ•จ์ˆ˜(count, sum, avg, min, max)
    *๋ชจ๋“  ์ง‘๊ณ„ ํ•จ์ˆ˜๋Š” null ์ œ์™ธ (์˜ˆ์™ธ: count(*))
  • Having
    group by๋กœ ๋ฌถ์€ ์กฐ๊ฑด์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜
    MySQL์—์„œ๋Š” Having์ด ๋งˆ์ง€๋ง‰์— 
    (Where๋Š” ๊ทธ๋ฃน ๋ฌถ์ด๊ธฐ ์ „์— ํ•„ํ„ฐ๋ง / having์€ ๊ทธ๋ฃน๋œ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋‹ค์‹œ ํ•„ํ„ฐ๋ง)
  • ์กฐ๊ฑด๋ถ€ ์ง‘๊ณ„
    : group by๋กœ ๋งŒ๋“  ๊ทธ๋ฃน ์•ˆ์—์„œ, 'ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ํ–‰๋งŒ' ์„ธ๊ฑฐ๋‚˜ ํ•ฉ์‚ฐํ•˜๋Š” ํŒจํ„ด
SUM(CASE WHEN ์กฐ๊ฑด THEN 1 ELSE 0 END)-- ์กฐ๊ฑด ๋งŒ์กฑ '๊ฑด์ˆ˜'

 


2. SQL ๋ผ์ด๋ธŒ ์„ธ์…˜ 3ํšŒ์ฐจ
# ๋ฐ์ผ๋ฆฌ ๊ณผ์ œ

**Table: order_items ์„ importํ•  ๋•Œ, [Set empty strings to NULL] ํ•ญ๋ชฉ์„ ์ฒดํฌํ•ด์„œ ๋ถˆ๋Ÿฌ์™”๋Š”๋ฐ ์‹œ์Šคํ…œ์˜ ์˜ค๋ฅ˜ ๋•Œ๋ฌธ์ธ์ง€ ์ด ์„ค์ •์ด ์ž˜ ์•ˆ ๋จนํ˜”๋˜ ๋ชจ์–‘์ด๋‹ค. ๋ฌธ์ œ๋ฅผ ํ‘ธ๋Š” ๋‚ด๋‚ด ์ตœ์ข… ๊ฐ’์ด ์˜์›ํžˆ ๋‹ฌ๋ผ์„œ ํ•˜๋ฃจ ์ข…์ผ ์ด ๋ฌธ์ œ๋งŒ ํ’€์—ˆ๋‹ค ใ…œใ…œ

 

๊ณผ์ œ1)
์ฃผ๋ฌธ ์ƒํƒœ๋ณ„ ์šด์˜ ์ง€ํ‘œ(์•„์ดํ…œ/์ฃผ๋ฌธ ํ˜ผํ•ฉ)

--์ฃผ๋ฌธ ํ•œ ๊ฑด ๋‹น ๋ช‡ ๊ฐœ์˜ ์ƒํ’ˆ์„ ๊ตฌ๋งคํ–ˆ๋Š”์ง€
select order_status,
       COUNT(DISTINCT order_id) as orders,
       count(*) as item_rows,
       round(count(*)/COUNT(DISTINCT order_id),2)
from basic.order_items
group by order_status
having count(*)>=10
order by orders DESC;

๊ณผ์ œ2)

์ฑ„๋„๋ณ„ ์ทจ์†Œ/ํ™˜๋ถˆ์œจ(์ฃผ๋ฌธ ๋ ˆ๋ฒจ, ์กฐ๊ฑด๋ถ€ ์ง‘๊ณ„)

--1์ฐจ ์‹œ๋„
select channel,
       count(DISTINCT order_id) as total_orders,
       count(DISTINCT case when order_status='cancelled' then 1 else 0 end) as cancelled_orders,
       count(DISTINCT case when order_status='refunded' then 1 else 0 END) as refunded_orders,
       count(DISTINCT case when order_status='cancelled' then 1
                         when order_status='refunded' then 1
                         else 0 end) as cancel_or_refund_orders,
       round((count(DISTINCT case when order_status='cancelled' then 1
                                  when order_status='refunded' then 1
                                  else 0 end)/count(DISTINCT order_id))*100, 1) as cancel_or_refund_rate_pct
from basic.order_items
group by channel
having count(DISTINCT order_id)>=20
order by cancel_or_refund_rate_pct DESC, channel ASC;

--2์ฐจ ์‹œ๋„
select channel,
       count(DISTINCT order_id) as total_orders,
       count(DISTINCT case when order_status='cancelled' then 1 else null end) as cancelled_orders,
       count(distinct case when order_status='refunded' then 1 else null end) as refunded_orders,
       count(distinct case when order_status<>'completed' then 1 else null end) as cancel_or_refund_orders,
       round(count(distinct case when order_status<>'completed' then 1 else null end)/count(DISTINCT order_id)*100, 1) as cancel_or_refund_rate_pct
from basic.order_items
group by channel
having count(DISTINCT order_id)>=20
order by cancel_or_refund_rate_pct DESC, channel ASC;

--๐Ÿ“Œ์ตœ์ข… ๋‹ต๋ณ€
select channel,
       count(distinct order_id) as total_orders,
       count(distinct case when order_status='cancelled' then order_id end) as cancelled_orders,
       count(distinct case when order_status='refunded' then order_id end) as refunded_orders,
       count(distinct case when order_status in ('cancelled', 'refunded') then order_id end) as cancel_or_refund_orders,
       round(count(distinct case when order_status in ('cancelled', 'refunded') then order_id end)
             /count(distinct order_id)*100
             ,1) as cancel_or_refund_rate_pct
from basic.order_items
group by channel 
having count(distinct order_id)>=20
order by cancel_or_refund_rate_pct DESC, channel ASC;
<์˜ค๋‹ต๋…ธํŠธ>
count(DISTINCT case when order_status='cancelled' then 1 else null end)
- order_status๊ฐ€ cancelled์ด๋ฉด 1์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ -> distinct ๋•Œ๋ฌธ์— ๋ชจ๋“  ํ–‰์— ๋ฐ˜ํ™˜๋˜์—ˆ๋˜ 1์ด ์ค‘๋ณต์ œ๊ฑฐ ๋˜๋ฉด์„œ ๊ฐ’์ด 1๋กœ๋งŒ ๋‚˜์˜ด.
- ๊ทธ๋ ‡๋‹ค๊ณ  distinct๋ฅผ ์ง€์šฐ๋ฉด -> ์ค‘๋ณต๋˜๋Š” order_id๋ฅผ ๊ฑธ๋Ÿฌ๋‚ผ ์ˆ˜ ์—†์Œ.

 

๊ณผ์ œ3) ๋‚œ์ด๋„ Hell ๐Ÿ”ฅ๐Ÿ˜‚

์ง€์—ญ๋ณ„ ์ฟ ํฐ ์‚ฌ์šฉ๋ฅ (์ฃผ๋ฌธ ๋ ˆ๋ฒจ + NULL ํ™œ์šฉ)

*์ฃผ๋ฌธ ๋ ˆ๋ฒจ: ์ฃผ๋ฌธ 1๊ฑด์„ 1๋กœ ์„ธ๋Š” ๊ธฐ์ค€(: order_id๊ฐ€ ๊ณ ์œ ๊ฐ’ = distinct ์‚ฌ์šฉ)

--''๊ฐ’์ด ์žˆ๋Š” ๊ฒฝ์šฐ
SELECT
    region,
    COUNT(DISTINCT order_id) AS total_orders,
    COUNT(DISTINCT CASE WHEN (case when coupon_code='' then null else coupon_code end) is not null
                             THEN order_id END) AS coupon_orders,
    round(COUNT(DISTINCT CASE WHEN (case when coupon_code='' then null else coupon_code end) is not null THEN order_id END)/count(distinct order_id)*100, 1) as coupon_order_pct,
    COUNT(DISTINCT case when 
    			(case when coupon_code='' then null else coupon_code end)
    			is null then order_id end) as no_coupon_orders,
    COUNT(distinct case when delivery_type='pickup' then order_id end) as pickup_orders
from basic.order_items
group by region
having COUNT(DISTINCT CASE WHEN (case when coupon_code='' then null else coupon_code end) is not null THEN order_id END) >= 5
order by coupon_order_pct DESC, coupon_orders DESC;

--''๊ฐ’์„ null๋กœ ์„ค์ •ํ•œ ๊ฒฝ์šฐ
select region,
       count(distinct order_id) as total_orders,
       count(distinct case when coupon_code is not null then order_id end) as coupon_orders,
       round(count(distinct case when coupon_code is not null then order_id end)/count(distinct order_id)*100
       		,1) as coupon_order_pct,
       count(distinct case when coupon_code is null then order_id end) as no_coupon_orders,
       count(distinct case when delivery_type='pickup' then order_id end)as pickup_orders
from basic.order_items
group by region
having  count(distinct case when coupon_code is not null then order_id end) >= 5
order by coupon_order_pct DESC, coupon_orders DESC;
<์˜ค๋‹ต๋…ธํŠธ> 
coupon_code = '' ์—์„œ ''์„ null๋กœ ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ, null์˜ ๊ฐœ์ˆ˜๋ฅผ ์ƒˆ๋Š” count ํ•จ์ˆ˜ ๋‚ด์—์„œ ''์„ null๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” case when๋ฌธ์„ ํ•œ๋ฒˆ ๋” ์‚ฌ์šฉํ•ด์•ผ ํ•จ.
COUNT(DISTINCT CASE WHEN (case when coupon_code='' then null else coupon_code end) is not null THEN order_id END) AS coupon_orders,

 

๊ณผ์ œ4)

์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ “๋งค์ถœ + ๋ฐ˜ํ’ˆ + ๋ฆฌ๋ทฐ”(์™„๋ฃŒ ์ฃผ๋ฌธ ๊ธฐ๋ฐ˜)

SELECT product_category,
       COUNT(order_id) AS completed_items,
       ROUND(SUM(unit_price*quantity*(1-discount_rate)), 0) AS net_sales,
       COUNT(distinct CASE WHEN is_returned=1 then order_id end) AS returned_items,
       round(COUNT(distinct CASE WHEN is_returned=1 then order_id end) / COUNT(order_id)*100, 1) AS return_rate_pct,
       count(review_score) as review_cnt,
       round(count(review_score)/COUNT(order_id)*100, 1) as review_rate_pct
FROM basic.order_items
WHERE order_status='completed'
GROUP BY product_category
having COUNT(distinct order_id)>=15
order by net_sales DESC;

# ๋ฐ์ผ๋ฆฌ ํ€ด์ฆˆ

## Q1. `COUNT(review_score)`๋Š” ๋ฌด์—‡์„ ์„ธ๋‚˜์š”?
A. ์ „์ฒด ํ–‰ ์ˆ˜
B. `review_score`๊ฐ€ **NULL์ด ์•„๋‹Œ ํ–‰ ์ˆ˜**
C. `review_score`์˜ ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ’ ๊ฐœ์ˆ˜
D. `review_score`์˜ ํ‰๊ท 

## Q2. ๋‹ค์Œ ์ค‘ ์˜ฌ๋ฐ”๋ฅธ ์„ค๋ช…์€?
A. `WHERE`๋Š” ์ง‘๊ณ„ํ•จ์ˆ˜ ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
B. `HAVING`์€ ๊ทธ๋ฃน์ด ๋งŒ๋“ค์–ด์ง€๊ธฐ ์ „์— ์ ์šฉ๋œ๋‹ค
C. `HAVING`์€ ๊ทธ๋ฃน(์ง‘๊ณ„ ๊ฒฐ๊ณผ)์— ์กฐ๊ฑด์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
D. `WHERE`๋Š” ๊ทธ๋ฃน์„ ํ•„ํ„ฐ๋งํ•œ๋‹ค

## Q3. ๋‹ค์Œ ์ฟผ๋ฆฌ๊ฐ€ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋Š”?

SELECT product_category, product_name, SUM(unit_price*quantity)
FROM basic.order_items
WHERE order_status='completed'
GROUP BY product_category;


A. `SUM()`์€ SELECT์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค
B. `product_name`์ด GROUP BY์—๋„ ์—†๊ณ  ์ง‘๊ณ„๋„ ์•„๋‹ˆ๋ผ์„œ
C. `WHERE`์ ˆ์ด ์—†์–ด์„œ
D. `ORDER BY`๊ฐ€ ์—†์–ด์„œ

 

## Q4. ๋‹ค์Œ ์ค‘ `WHERE`์— ๋Œ€ํ•œ ์„ค๋ช…์œผ๋กœ ๋งž๋Š” ๊ฒƒ์€?
A. ๊ทธ๋ฃน ์ƒ์„ฑ ํ›„์— ์ ์šฉ๋œ๋‹ค
B. ์ง‘๊ณ„ํ•จ์ˆ˜๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค
C. ํ–‰(row) ๋‹จ์œ„ ํ•„ํ„ฐ์— ์‚ฌ์šฉ๋œ๋‹ค
D. ๊ทธ๋ฃน ๋‹จ์œ„ ํ•„ํ„ฐ์— ์‚ฌ์šฉ๋œ๋‹ค

## Q5. `GROUP BY` ์—†์ด ์ง‘๊ณ„ ํ•จ์ˆ˜๋งŒ ์“ฐ๋ฉด ๊ฒฐ๊ณผ๋Š” ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”?
A. ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค
B. ๊ฐ ํ–‰์ด ํ•œ ๊ทธ๋ฃน์ด ๋œ๋‹ค
C. ์ „์ฒด ํ–‰์ด ํ•˜๋‚˜์˜ ์ง‘๊ณ„ ๊ทธ๋ฃน์œผ๋กœ ์ฒ˜๋ฆฌ๋œ๋‹ค
D. DISTINCT๊ฐ€ ์ž๋™์œผ๋กœ ์ ์šฉ๋œ๋‹ค


3. ์ง๋ฌด ๋ฐœํ‘œ ํ”ผ๋“œ๋ฐฑ

  • ๋ฐœํ‘œ์ž๋ฃŒ๋ฅผ ๊ตฌ์กฐ์™€ ํ•ด์„œ ์‹œ๊ฐ์ ์œผ๋กœ ํšจ์œจ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๋Š” ๊ฒŒ ์ค‘์š”!
  • ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜๋ˆˆ ๊ตฐ์ง‘๋ณ„๋กœ ํŠน์ง•์„ ๋‚˜๋ˆ ์„œ(๋ธŒ๋žœ๋”ฉ ํ•ด์„œ) ๊ฒฝ์˜์ง„์—๊ฒŒ ํ™•! ์ „๋‹ฌํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•˜๋‹ค
  • ๋ฐœํ‘œ ์–ดํˆฌ: ๋งํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ vs ์•„๋‚˜์šด์„œ์ฒ˜๋Ÿผ ๋”ฑ๋”ฑ
  • ์‹ค์ œ ๋ฐœํ‘œ ํ™˜๊ฒฝ์„ ํŒŒ์•…ํ•ด์„œ ์ค€๋น„ํ•œ ์ž๋ฃŒ๊ฐ€ ์ž˜ ๋ณด์—ฌ์ง€๋Š”์ง€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€๋ฅผ ๊ณ ๋ คํ•˜๊ธฐ

<๋‹ค์Œ์ฃผ ํ•™์Šต ๊ณ„ํš>

  • ์ฃผ๋ง ์ค‘ SQL ์‚ฌ์ „์บ ํ”„ ํ€˜์ŠคํŠธ ์ง„ํ–‰ํ•  ์˜ˆ์ • (์™„๋ฃŒ)
    https://tjsdud7032.tistory.com/17
  • SQL ๋‚œ์ด๋„ ์˜ฌ๋ผ๊ฐ„ ๋ผ์ด๋ธŒ ์„ธ์…˜ ์ง„ํ–‰ ์˜ˆ์ •