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 까지 계산해야 되기 때문이다.