서브쿼리(Subquery)란?
- 하나의 SQL 문 안에 포함된 또 다른 SELECT 쿼리
- 중간 결과를 생성하여 바깥 쿼리에서 재사용하는 구조
-- 기본 구조
SELECT ...
FROM (
SELECT ...
FROM 테이블
) 별칭;
왜 쓰는가?
- 집계 → 조건 분리
- GROUP BY로 집계한 결과는 바로 WHERE/CASE에서 쓰기 어려움
- 그래서 먼저 서브쿼리로 결과를 만든 뒤, 바깥에서 조건/분류 처리
- 계산 단계 분리 (가독성)
- 재사용 가능한 '임시 테이블'
주의할 점
- 별칭(alias) 필수 → 없으면 오류
- 성능 이슈
- 서브쿼리 많아지면 성능 저하 가능(특히 중첩 서브쿼리 주의)
- 가독성 vs 성능
- 단순 쿼리 → 굳이 필요 없음
- 집계 후 조건 처리 → 서브쿼리 필수
-- 예시. 음식 준비시간이 25분 초과한 데이터 가져오기
SELECT
order_id,
restaurant_name,
food_preparation_time
FROM
(
SELECT
order_id,
restaurant_name,
food_preparation_time
FROM
food_orders
WHERE
food_preparation_time > 25 -- 준비시간이 25분 초과인 데이터만 추출
) a;
// -------------------
/*
실습 1. 음식점의 평균 단가별 segmentation 을 진행하고, 그룹에 따라 수수료 연산하기
(수수료 구간 -
5000원 미만 0.05%
5000원 이상 ~ 20,000원 미만 1%
20000원 이상 ~ 30,000원 미만:2%
30000원 이상 3%)
*/
SELECT
restaurant_name,
price_per_plate * ratio_of_add "수수료" -- 평균 단가 × 수수료율 = 수수료 계산
FROM
(
SELECT
restaurant_name,
CASE
WHEN price_per_plate < 5000 THEN 0.005 -- 5000원 미만 → 0.5%
WHEN price_per_plate BETWEEN 5000 AND 19999 THEN 0.01 -- 5000~19999 → 1%
WHEN price_per_plate BETWEEN 20000 AND 29999 THEN 0.02 -- 20000~29999 → 2%
ELSE 0.03 -- 30000 이상 → 3%
END ratio_of_add,
price_per_plate
FROM
(
SELECT
restaurant_name,
AVG(price / quantity) price_per_plate -- 음식점별 평균 단가 계산
FROM
food_orders
GROUP BY 1 -- 음식점별 그룹화
) a
) b;
// -------------------
-- 실습 2. 음식점의 지역과 평균 배달시간으로 segmentation 하기
SELECT
restaurant_name,
sido,
CASE
WHEN avg_time <= 20 THEN '<=20' -- 평균 배달시간 20분 이하
WHEN avg_time > 20 AND avg_time <= 30 THEN '20<x<=30' -- 20분 초과 30분 이하
WHEN avg_time > 30 THEN '>30' -- 30분 초과
END time_segment
FROM
(
SELECT
restaurant_name,
SUBSTRING(addr, 1, 2) sido, -- 주소 앞 2글자로 시/도 추출
AVG(delivery_time) avg_time -- 음식점별 평균 배달시간 계산
FROM
food_orders
GROUP BY 1, 2 -- 음식점 + 지역 기준 그룹화
) a;
// -------------------
/*
실습 3.
음식 타입별 지역별 총 주문수량과 음식점 수를 연산하고, 주문수량과 음식점수 별 수수료율을 산정하기
(음식점수 5개 이상, 주문수 30개 이상 → 수수료 0.5%
음식점수 5개 이상, 주문수 30개 미만 → 수수료 0.8%
음식점수 5개 미만, 주문수 30개 이상 → 수수료 1%
음식점수 5개 미만, 주문수 30개 미만 → 수수로 2%)
*/
SELECT
cuisine_type,
total_quantity,
count_of_restautant,
CASE
WHEN count_of_restautant >= 5
AND total_quantity >= 30 THEN 0.005 -- 음식점 5개 이상 + 주문수 30개 이상
WHEN count_of_restautant >= 5
AND total_quantity < 30 THEN 0.008 -- 음식점 5개 이상 + 주문수 30개 미만
WHEN count_of_restautant < 5
AND total_quantity >= 30 THEN 0.01 -- 음식점 5개 미만 + 주문수 30개 이상
WHEN count_of_restautant < 5
AND total_quantity < 30 THEN 0.02 -- 음식점 5개 미만 + 주문수 30개 미만
END rate
FROM
(
SELECT
cuisine_type,
SUM(quantity) total_quantity, -- 음식 타입별 총 주문수량
COUNT(DISTINCT restaurant_name) count_of_restautant -- 음식 타입별 음식점 수
FROM
food_orders
GROUP BY 1 -- 음식 타입별 그룹화
) a;
// -------------------
/*
실습 4.
음식점의 총 주문수량과 주문 금액을 연산하고, 주문 수량을 기반으로 수수료 할인율 구하기
(할인조건 :
수량이 5개 이하 → 10%
수량이 15개 초과, 총 주문금액이 300000 이상 → 0.5%
이 외에는 일괄 1%)
*/
SELECT
cuisine_type,
sido,
total_quantity,
count_of_restaurant,
CASE
WHEN count_of_restaurant >= 5 AND total_quantity >= 30 THEN 0.005
WHEN count_of_restaurant >= 5 AND total_quantity < 30 THEN 0.008
WHEN count_of_restaurant < 5 AND total_quantity >= 30 THEN 0.01
ELSE 0.02
END AS rate
FROM
(
SELECT
cuisine_type,
SUBSTRING(addr, 1, 2) AS sido, -- 지역 추출
SUM(quantity) AS total_quantity, -- 음식 타입+지역별 총 주문수량
COUNT(DISTINCT restaurant_name) AS count_of_restaurant -- 음식 타입+지역별 음식점 수
FROM
food_orders
GROUP BY 1, 2
) a;
-- 이번 실습 핵심 패턴
SELECT
최종출력컬럼,
CASE ... END
FROM
(
SELECT
그룹기준,
집계값
FROM 테이블
GROUP BY 기준
) a;
-- 먼저 집계/가공 → 집계 결과를 기준으로 분류/계산