<์ค๋์ ํ์ต>
- 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 ๋์ด๋ ์ฌ๋ผ๊ฐ ๋ผ์ด๋ธ ์ธ์ ์งํ ์์
'์คํ๋ฅดํ ๋ด์ผ๋ฐฐ์์บ ํ(25.12.01~)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ์คํ๋ฅดํ ๋ด์ผ๋ฐฐ์์บ ํ_๋ณธ์บ ํ_data11๊ธฐ ๊น์ ์_TIL_Day 5 (0) | 2025.12.29 |
|---|---|
| SQL ๊ฑท๊ธฐ๋ฐ ๋ฌธ์ ํตํฉ (1) | 2025.12.27 |
| ์คํ๋ฅดํ ๋ด์ผ๋ฐฐ์์บ ํ_๋ณธ์บ ํ_data11๊ธฐ ๊น์ ์_TIL_Day 3 (0) | 2025.12.24 |
| ์คํ๋ฅดํ ๋ด์ผ๋ฐฐ์์บ ํ_๋ณธ์บ ํ_data11๊ธฐ ๊น์ ์_TIL_Day 2 (0) | 2025.12.23 |
| ์คํ๋ฅดํ ๋ด์ผ๋ฐฐ์์บ ํ_๋ณธ์บ ํ_data11๊ธฐ ๊น์ ์_TIL_Day 1 (1) | 2025.12.22 |