MSSQL LAB
MSSQL 1.7 · 1부. 기초

정렬과 상위 N개

ORDER BY 로 차례를 정하고 TOP · OFFSET FETCH 로 끊어 냅니다. 적지 않으면 차례가 없다는 것도 실제로 봅니다.

예상 학습 시간 18분 난이도 입문
개념 설명

적지 않으면 순서가 없습니다

ORDER BY 를 적지 않은 조회는 어떤 차례로 나올지 정해져 있지 않습니다. 넣은 차례대로 나오는 것처럼 보여도 그것은 우연입니다.

데이터베이스는 그때그때 가장 빠른 방법으로 자료를 읽습니다. 인덱스가 생기거나 자료가 늘거나 서버가 바쁜 정도가 달라지면 읽는 길이 바뀌고, 그러면 나오는 차례도 함께 바뀝니다. 어제까지 맞던 화면이 오늘 뒤섞이는 일이 여기서 생깁니다.

차례가 중요하다면 반드시 적으십시오. 이 단원의 예제가 모두 ORDER BY 를 달고 있는 까닭입니다.

최소 예제

오름차순과 내림차순

기본은 오름차순(ASC)이라 적지 않아도 됩니다. 거꾸로 하려면 DESC 를 붙입니다.

SQL
SELECT TOP 4 num, name, sort_no
FROM Board.CATEGORIES
ORDER BY sort_no DESC, num;
결과
numnamesort_no
5모집5
10기타5
4질문4
9오류4
(4개 행이 영향을 받음)

sort_no 가 같은 행이 둘씩 있는데, 뒤에 적은 num 이 그 안에서 차례를 정했습니다. 앞에 적은 것이 먼저 적용되고, 같을 때만 뒤엣것을 봅니다.

DESC바로 앞의 열에만 걸립니다. 위 예에서 num 은 오름차순입니다. 둘 다 거꾸로 하려면 각각 적어야 합니다.

여러 열로 묶어 보기

SQL
SELECT board_num, sort_no, name
FROM Board.CATEGORIES
ORDER BY board_num, sort_no;
결과
board_numsort_noname
21잡담
22후기
23정보
24질문
25모집
31설치
32쿼리
33성능
34오류
35기타
(10개 행이 영향을 받음)

게시판별로 묶이고 그 안에서 정한 차례대로 나옵니다. 화면의 말머리 목록이 이렇게 만들어집니다.

상세 사용법

NULL 은 어디에 놓이나

SQL Server 는 NULL가장 작은 값으로 봅니다. 그래서 오름차순이면 맨 앞에, 내림차순이면 맨 뒤에 놓입니다.

SQL
SELECT TOP 6 num, nickname, email
FROM Member.USERS
ORDER BY email, num;
결과
numnicknameemail
5최지우NULL
9장미래NULL
14신우진NULL
19구하람NULL
20안지호ahn@example.com
17백나윤baek@example.com
(6개 행이 영향을 받음)

전자 메일이 없는 넷이 먼저 나왔습니다. 값이 있는 행을 먼저 보고 싶다면 정렬 열을 하나 더 두어야 합니다.

ORDER BY CASE WHEN email IS NULL THEN 1 ELSE 0 END, email

CASE 는 2.8 에서 다룹니다. 여기서는 정렬 기준을 계산해서 만들 수도 있다는 것만 봐 두십시오.

상세 사용법

별칭으로 정렬하기

SELECT 에서 붙인 이름을 ORDER BY 에 그대로 사용할 수 있습니다. 계산한 열을 정렬할 때 편합니다.

SQL
SELECT TOP 3 title, hit_count * 2 AS 두배
FROM Board.POSTS
ORDER BY 두배 DESC;
결과
title두배
윈도 함수 질문드립니다996
외래 키 실무에서 겪은 일994
실행 계획 초보자가 자주 하는 실수990
(3개 행이 영향을 받음)

별칭을 사용할 수 있는 절은 ORDER BY 뿐입니다. WHERE 에서는 사용할 수 없습니다 — 실행되는 차례가 달라서인데, 그 차례는 2.3 에서 다룹니다.

열 번호로 ORDER BY 2 DESC 처럼 적을 수도 있지만 권하지 않습니다. 나중에 SELECT 에 열을 끼워 넣으면 정렬 기준이 조용히 다른 열로 옮겨 갑니다.

상세 사용법

앞에서 몇 개만 — TOP

TOP 은 정렬한 결과에서 앞의 몇 행만 남깁니다. 동점이 있으면 그중 일부만 나오고 나머지는 잘립니다.

SQL
SELECT TOP 4 num, hit_count, title
FROM Board.POSTS
WHERE hit_count BETWEEN 194 AND 198
ORDER BY hit_count DESC;
결과
numhit_counttitle
370198[답글] 저장 프로시저 실무에서 겪은 일
314198윈도 함수 속도가 느립니다
171197외래 키 문서를 읽어도 모르겠습니다
28196임시 테이블 정리해 봤습니다
(4개 행이 영향을 받음)

조회수 196 인 글이 둘인데 하나만 나왔습니다. 넷째 자리에서 끊겼기 때문입니다. 동점을 함께 내려면 WITH TIES 를 붙입니다.

SQL
SELECT TOP 4 WITH TIES num, hit_count, title
FROM Board.POSTS
WHERE hit_count BETWEEN 194 AND 198
ORDER BY hit_count DESC;
결과
numhit_counttitle
314198윈도 함수 속도가 느립니다
370198[답글] 저장 프로시저 실무에서 겪은 일
171197외래 키 문서를 읽어도 모르겠습니다
28196임시 테이블 정리해 봤습니다
390196[답글] 정규화 개념이 헷갈립니다
(5개 행이 영향을 받음)

4를 적었는데 5행이 나왔습니다. 마지막 값과 같은 행을 모두 데려오기 때문입니다. 순위표처럼 "공동 4위" 를 함께 보여야 하는 자리에 사용합니다.

두 결과에서 198 의 차례가 다릅니다

위 두 결과를 나란히 보십시오. 조회수 198 인 두 행의 차례가 서로 뒤바뀌어 있습니다.

문장198 인 두 행의 차례
TOP 4370 → 314
TOP 4 WITH TIES314 → 370

ORDER BYhit_count 만 적었으니 같은 값 안에서의 차례는 정해진 것이 없습니다. 데이터베이스가 그때 선택한 읽는 방법에 따라 달라집니다.

차례를 못 박으려면 정렬 열을 하나 더 두십시오. ORDER BY hit_count DESC, num 처럼 값이 겹치지 않는 열을 뒤에 붙이면 언제 돌려도 같은 결과가 나옵니다. 페이징에서 특히 중요합니다 — 차례가 흔들리면 어떤 글은 두 쪽에 나오고 어떤 글은 어느 쪽에도 안 나옵니다.

상세 사용법

가운데부터 몇 개 — OFFSET FETCH

TOP 은 앞에서만 잘라 냅니다. 3쪽을 보여 주려면 앞의 10건을 건너뛰고 다음 5건을 가져와야 하는데, 그때 사용합니다.

SQL
SELECT num, title
FROM Board.POSTS
ORDER BY num
OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY;
결과
numtitle
11외래 키 질문드립니다
12정규화 질문드립니다
13집계 함수 질문드립니다
14윈도 함수 질문드립니다
15CTE 질문드립니다
(5개 행이 영향을 받음)

OFFSET 은 건너뛸 행 수, FETCH NEXT 는 가져올 행 수입니다. 한 쪽에 5건이면 3쪽은 (3 - 1) * 5 = 10 을 건너뜁니다.

ORDER BY 없이는 사용할 수 없습니다. 차례가 정해져야 몇 번째부터인지도 뜻이 생기기 때문입니다.

글이 수십만 건이 되면 이 방식이 느려집니다. 건너뛸 행을 세느라 앞부분을 모두 읽기 때문인데, 그 대안은 5.6 에서 다룹니다.

상세 사용법

게시판 목록은 이렇게 정렬합니다

1.2 에서 본 계층 열이 여기서 쓰입니다. 덩어리를 최신순으로 놓고, 덩어리 안에서는 답글 차례대로 냅니다.

SQL
SELECT TOP 6 group_num, depth, sort_no, title
FROM Board.POSTS
WHERE board_num = 2
ORDER BY group_num DESC, sort_no;
결과
group_numdepthsort_notitle
34600저장 프로시저 예제 모음
34500실행 계획 예제 모음
34511[답글] 실행 계획 예제 모음
34522[답글] [답글] 실행 계획 예제 모음
34400백업 예제 모음
34300페이징 예제 모음
(6개 행이 영향을 받음)

답글이 원글 바로 아래 붙어 나옵니다. 정렬 열 두 개로 계층이 표현됐습니다 — 부모를 따라 거슬러 올라가지 않아도 됩니다. 이 방식이 무엇을 얻고 무엇을 포기했는지는 3.9 에서 다른 방식들과 비교합니다.

실습 문제

직접 해보기

1. 가장 최근 회원 다섯 명 난이도 하

가입일이 늦은 회원부터 다섯 명을 닉네임과 함께 보세요.

SELECT TOP 5 num, nickname, reg_date FROM Member.USERS ORDER BY reg_date DESC, num; -- num 을 뒤에 붙인 것은 같은 날 가입한 회원이 생겼을 때 -- 차례가 흔들리지 않게 하려는 것입니다.
2. 2쪽 가져오기 난이도 중

질문답변 게시판(board_num = 3)의 글을 번호가 큰 것부터 한 쪽에 10건씩 나눌 때, 2쪽에 나올 글을 가져와 보세요.

건너뛸 행 수는 (쪽 번호 - 1) × 한 쪽 건수 입니다.
SELECT num, title FROM Board.POSTS WHERE board_num = 3 ORDER BY num DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY; -- num 은 겹치지 않는 열이라 차례가 흔들리지 않습니다. -- 만약 hit_count 로만 정렬했다면 동점 행이 쪽을 오갈 수 있습니다.
요약
  • ORDER BY 를 적지 않으면 차례가 정해져 있지 않습니다. 지금 맞아 보여도 언제든 바뀝니다.
  • 앞에 적은 열이 먼저 적용되고, DESC바로 앞의 열에만 걸립니다.
  • NULL 은 가장 작은 값으로 보아 오름차순에서 맨 앞에 놓입니다.
  • TOP 은 동점을 자릅니다. 함께 내려면 WITH TIES 입니다.
  • 같은 값 안의 차례를 못 박으려면 겹치지 않는 열을 정렬에 하나 더 붙이십시오.
  • 가운데부터 가져올 때는 OFFSET FETCH 이고, ORDER BY 가 반드시 있어야 합니다.