MSSQL 2.8 · 2부. 조회 심화

조건식

CASE · IIF · CHOOSE 로 값에 따라 다른 것을 냅니다. 집계 안에 넣어 한 줄에 여러 셈을 내는 방법이 핵심입니다.

예상 학습 시간 18분 난이도 초급
개념 설명

값에 따라 다른 것을 냅니다

지금까지는 담긴 값을 그대로 내거나 함수로 가공했습니다(1.10). CASE조건을 보고 낼 값을 정합니다.

두 가지 형태가 있습니다. 조건을 하나씩 적는 것과, 열 하나의 값을 여러 개와 비교하는 것입니다.

SQL
-- 조건을 하나씩 적습니다(검색 CASE)
SELECT TOP 6 num, hit_count,
       CASE WHEN hit_count >= 400 THEN N'높음'
            WHEN hit_count >= 200 THEN N'보통'
            ELSE N'낮음' END AS 등급
FROM Board.POSTS
WHERE num IN (1, 60, 70, 140, 214, 300)
ORDER BY num;
결과
numhit_count등급
17낮음
60420높음
70490높음
140480높음
214498높음
300100낮음
(6개 행이 영향을 받음)

위에서부터 보다가 처음 참인 것에서 멈춥니다. 그래서 범위 조건을 적을 때는 차례가 중요합니다. 위 문장에서 400 과 200 을 바꿔 적으면 420 짜리 글도 "보통" 이 됩니다.

값 하나를 여럿과 견줄 때

SQL
SELECT TOP 3 num, board_num,
       CASE board_num WHEN 1 THEN N'공지'
                       WHEN 2 THEN N'자유'
                       WHEN 3 THEN N'질문' END AS 게시판
FROM Board.POSTS
ORDER BY num;
결과
numboard_num게시판
12자유
22자유
32자유
(3개 행이 영향을 받음)

짧지만 같음(=) 비교만 됩니다. 범위나 IS NULL 을 써야 하면 검색 형태로 적어야 합니다.

이 예제는 설명을 위한 것입니다. 실제로는 게시판 이름을 문장에 박아 넣지 말고 Board.BOARDS 와 조인하십시오(2.1). 이름이 바뀌면 문장을 찾아 고쳐야 하기 때문입니다.

상세 사용법

ELSE 를 빠뜨리면 NULL

SQL
SELECT CASE WHEN 1 = 2 THEN N'참' END              AS else없음,
       CASE WHEN 1 = 2 THEN N'참' ELSE N'거짓' END AS else있음;
결과
else없음else있음
NULL거짓
(1개 행이 영향을 받음)

어느 조건에도 걸리지 않으면 NULL 입니다. 그 값을 이어 붙이거나 셈에 넣으면 결과가 통째로 NULL 이 됩니다(1.5 · 1.9). 빠뜨리기 쉬우니 ELSE 를 습관으로 적으십시오.

짧게 줄인 형태

SQL
SELECT IIF(10 > 5, N'크다', N'작다')            AS iif결과,
       CHOOSE(2, N'첫째', N'둘째', N'셋째')  AS choose결과,
       CHOOSE(9, N'첫째', N'둘째')          AS 범위밖;
결과
iif결과choose결과범위밖
크다둘째NULL
(1개 행이 영향을 받음)
함수같은 뜻의 CASE
IIF(조건, A, B)CASE WHEN 조건 THEN A ELSE B END
CHOOSE(n, …)n 번째를 냅니다. 범위를 벗어나면 NULL
COALESCE(A, B)CASE WHEN A IS NOT NULL THEN A ELSE B END (1.9)
NULLIF(A, B)CASE WHEN A = B THEN NULL ELSE A END (1.9)

IIFCHOOSE 는 SQL Server 전용입니다. 경우가 둘뿐이면 짧아서 좋지만, 셋 이상이면 CASE 가 읽기 좋습니다. 조건과 결과가 나란히 보이기 때문입니다.

최소 예제

조건부 집계 — 한 줄에 여러 셈

이 단원에서 가장 쓸모 있는 용법입니다. 집계 함수 안에 CASE 를 넣으면 조건마다 따로 셀 수 있습니다.

2.2 에서 게시판별 원글과 답글을 세려면 GROUP BY board_num, depth 로 묶어 일곱 줄이 나왔습니다. 그것을 게시판당 한 줄로 냅니다.

SQL
SELECT board_num,
       COUNT(*)                                          AS 전체,
       SUM(CASE WHEN depth = 0 THEN 1 ELSE 0 END)   AS 원글,
       SUM(CASE WHEN depth > 0 THEN 1 ELSE 0 END)   AS 답글,
       COUNT(CASE WHEN hit_count >= 400 THEN 1 END) AS 인기글
FROM Board.POSTS
GROUP BY board_num
ORDER BY board_num;
결과
board_num전체원글답글인기글
1353508
231521010539
31571055218
(3개 행이 영향을 받음)

두 가지 방식을 함께 보여 주었습니다.

  • SUM(CASE … THEN 1 ELSE 0 END) — 맞으면 1, 아니면 0 을 더합니다.
  • COUNT(CASE … THEN 1 END) — 맞으면 1, 아니면 NULL 인데 COUNT 가 NULL 을 빼고 세므로(2.2) 같은 결과가 됩니다. ELSE 를 적지 않는 것이 요령입니다.

둘 다 익혀 두십시오. 남의 코드에서 두 형태를 모두 보게 됩니다. 공지사항의 답글이 0 인 것은 그 게시판이 답글을 받지 않기 때문입니다(1.2).

이 방식으로 행을 열로 돌리는 것이 다음 단원의 PIVOT 입니다(2.9).

상세 사용법

정렬 기준을 계산해서 만들기

1.7 에서 NULL 이 오름차순 맨 앞에 놓이는 것을 보고, "값이 있는 것을 먼저 보려면 정렬 기준을 계산해야 한다" 고만 적었습니다. 그 방법이 CASE 입니다.

SQL
SELECT TOP 6 num, nickname, email
FROM Member.USERS
ORDER BY CASE WHEN email IS NULL THEN 1 ELSE 0 END, email;
결과
numnicknameemail
20안지호ahn@example.com
17백나윤baek@example.com
11한소망han@example.com
1홍길동hong@example.com
6정하늘jung@example.com
7강바다kang@example.com
(6개 행이 영향을 받음)

1.7 에서는 전자 메일이 없는 넷이 먼저 나왔는데, 이제 뒤로 갔습니다. 값이 있으면 0, 없으면 1 을 매겨 그것으로 먼저 정렬했기 때문입니다.

ORDER BY 에 적은 식은 결과에 나오지 않아도 됩니다. 화면에는 0 과 1 이 보이지 않습니다.

상세 사용법

WHERE 에서는 대개 필요 없습니다

CASEWHERE 에도 사용할 수 있지만, 같은 뜻을 AND · OR 로 적을 수 있다면 그쪽이 낫습니다.

SQL
-- 굳이 CASE 로
SELECT COUNT(*) AS case로 FROM Board.POSTS
WHERE CASE WHEN board_num = 1 THEN hit_count ELSE 0 END >= 400;

-- 같은 뜻, 더 짧고 인덱스도 사용합니다
SELECT COUNT(*) AS and로 FROM Board.POSTS
WHERE board_num = 1 AND hit_count >= 400;
결과
case로and로
88
(각각 1개 행)

결과는 같지만 앞엣것은 인덱스를 사용하지 못합니다. 열을 식으로 감쌌기 때문입니다(1.10 · 5.4). CASE낼 값을 정할 때 쓰고, 남길 행을 정하는 일은 WHERE 의 조건에 맡기십시오.

실습 문제

직접 해보기

1. 회원 목록에 표시 문구 붙이기 난이도 하

회원 목록에 전자 메일이 있으면 그 주소를, 없으면 미등록 을 내되 CASE 로 적어 보세요. 그다음 COALESCE 로도 적어 두 결과가 같은지 확인하십시오.

SELECT nickname, CASE WHEN email IS NULL THEN N'미등록' ELSE email END AS case로, COALESCE(email, N'미등록') AS coalesce로 FROM Member.USERS ORDER BY num; -- 같은 결과입니다. COALESCE 가 이 CASE 의 축약이기 때문입니다. -- 값이 없을 때 대신 낼 것을 정하는 일이라면 COALESCE 가 짧습니다.
2. 회원별 활동 요약 난이도 중

회원마다 작성한 글 수와 그중 원글 수·답글 수를 한 줄로 내고, 많이 작성한 사람부터 다섯 명만 보이게 해보세요.

집계 함수 안에 CASE 를 넣으면 조건마다 따로 셀 수 있습니다.
SELECT TOP 5 U.nickname, COUNT(*) AS 전체, SUM(CASE WHEN P.depth = 0 THEN 1 ELSE 0 END) AS 원글, SUM(CASE WHEN P.depth > 0 THEN 1 ELSE 0 END) AS 답글 FROM Board.POSTS AS P INNER JOIN Member.USERS AS U ON U.num = P.user_num GROUP BY U.nickname ORDER BY 전체 DESC, U.nickname; -- COUNT(CASE WHEN … THEN 1 END) 로 적어도 같습니다. -- 그때는 ELSE 를 적지 않습니다.
요약
  • CASE위에서부터 보다가 처음 참인 것에서 멈춥니다. 범위 조건은 차례가 중요합니다.
  • ELSE 가 없으면 NULL 입니다. 습관으로 적으십시오.
  • COALESCE · NULLIF · IIF 는 모두 CASE 의 축약입니다.
  • 집계 안의 CASE 로 한 줄에 여러 셈을 냅니다. SUM(CASE … THEN 1 ELSE 0 END) 또는 COUNT(CASE … THEN 1 END) 입니다.
  • ORDER BYCASE 를 두면 NULL 을 뒤로 보낼 수 있습니다.
  • WHERE 에서는 AND · OR 로 적으십시오. 인덱스를 사용하지 못합니다.