CASE 문은 프로그래밍의 if-else 처럼 조건부로 동적으로 각 행의 결과를 정할 수 있는 기능이다. 사용법 자체는 switch 문과 유사하다.
CASE의 종류에는 단순 CASE 문과 검색 CASE 문이 있다.
단순 CASE는 단일 컬럼을 대상으로 해당 컬럼의 값에 따라 분기처리 할 수 있는 방법이고,
검색 CASE는 각 조건별로 더 다양한 조건들을 넣어서 더 유연하게 분기처리 할 수 있는 방법이다.
-- 만약 주문의 status에 따라 아래처럼 표현해야 한다면 단순 CASE를 쓸 수 있다.
-- PENDING='주문 대기', COMPLETD='결제 완료', SHIPPED='배송중', CANCELLED='주문 취소'
SELECT
order_id,
CASE status
WHEN 'PENDING' THEN '주문 대기'
WHEN 'COMPLETD' THEN '결제 완료'
WHEN 'SHIPPED' THEN '배송중'
WHEN 'CANCELLED' THEN '주문 취소'
ELSE '알 수 없음'
END as status_korean
FROM
orders;
-- 만약 상품의 가격에 따라 아래처럼 표현해야 한다면 검색 CASE를 쓸 수 있다.
-- price >= 100000 ? 고가, price >= 50000 ? 중가, else ? 저가
SELECT
product_id,
name,
CASE
WHEN price >= 100000 THEN '고가'
WHEN price >= 50000 THEN '중가'
ELSE '저가'
END AS price_label
FROM
products;
CASE 문은 SELECT 말고도 ORDER BY, GROUP BY, WHERE 절에도 사용할 수 있다.
-- 위의 가격별 라벨링 쿼리에서 정렬도 고가 -> 중가 -> 저가 순으로 하고싶다면 아래처럼 할 수 있다.
SELECT
product_id,
name,
CASE
WHEN price >= 100000 THEN '고가'
WHEN price >= 50000 THEN '중가'
ELSE '저가'
END AS price_label
FROM
products
ORDER BY
CASE
WHEN price >= 100000 THEN 1
WHEN price >= 50000 THEN 2
ELSE 3
END;
price DESC;
-- 이렇게 하면 1 -> 2 -> 3 순서이므로 고가 -> 중가 -> 저가 순으로 정렬이 된다.
-- 그리고 같은 가격 등급이라면 다시 가격 내림차순으로 정렬하게 하였다.
-- 만약 유저의 나이에 따라 아래처럼 나누고 각 그룹별로 총 몇명이 있는지 보고싶다면 아래처럼 할수있다.
-- age >= 50 ? 장년, age >= 40 ? 중년, else ? 청년
SELECT
COUNT(*) as user_count,
CASE
WHEN age >= 50 THEN '장년'
WHEN age >= 40 THEN '중년'
ELSE '청년'
END AS age_level
FROM
users
GROUP BY
CASE
WHEN age >= 50 THEN '장년'
WHEN age >= 40 THEN '중년'
ELSE '청년'
END;
-- 결과는 { user_count : 10, age_level : '장년' } 처럼 각 그룹별로 결과가 나온다.
-- GROUP BY 에는 원칙적으로 SELECT의 별칭을 사용할 수 없지만, MySQL 포함 많은 DB가 예외적으로 허용한다.
SELECT
COUNT(*) as user_count,
CASE
WHEN age >= 50 THEN '장년'
WHEN age >= 40 THEN '중년'
ELSE '청년'
END AS age_level
FROM
users
GROUP BY
age_level;
-- 이렇게 원칙적으로는 안되지만 MySQL은 허용해주기 때문에 더 간결하게 쿼리를 쓸 수 있다.
이런식으로 쓰는 것 이외에도 SUM, COUNT 같은 집계 함수에도 사용할 수 있다.
만약 주문 데이터의 상태에 따라 아래처럼 피벗 테이블을 만들 때 유용하게 사용 할 수 있다.
| total_count | completed_count | shipped_count |
|---|---|---|
| 10 | 5 | 5 |
이런 형태로 만들려면 아래처럼 SELECT 에서 각 상태별로 서브쿼리를 만들어서 해결할 수도 있다.
SELECT
(SELECT COUNT(*) FROM orders) AS total_count,
(SELECT COUNT(*) FROM orders WHERE status = ‘COMPLETED’) AS completed_count
하지만 이렇게 각각의 컬럼별로 서브쿼리를 돌리면 당연히 성능적으로 버겁다. 하나의 쿼리별로 조회를 하고 COUNT 까지 계산해야 되기 때문이다.