정렬과 상위 N개
ORDER BY 로 차례를 정하고 TOP · OFFSET FETCH 로 끊어 냅니다. 적지 않으면 차례가 없다는 것도 실제로 봅니다.
적지 않으면 순서가 없습니다
ORDER BY 를 적지 않은 조회는 어떤 차례로 나올지
정해져 있지 않습니다. 넣은 차례대로 나오는 것처럼 보여도 그것은 우연입니다.
데이터베이스는 그때그때 가장 빠른 방법으로 자료를 읽습니다. 인덱스가 생기거나 자료가 늘거나 서버가 바쁜 정도가 달라지면 읽는 길이 바뀌고, 그러면 나오는 차례도 함께 바뀝니다. 어제까지 맞던 화면이 오늘 뒤섞이는 일이 여기서 생깁니다.
차례가 중요하다면 반드시 적으십시오. 이 단원의 예제가 모두
ORDER BY 를 달고 있는 까닭입니다.
오름차순과 내림차순
기본은 오름차순(ASC)이라 적지 않아도 됩니다. 거꾸로 하려면
DESC 를 붙입니다.
SELECT TOP 4 num, name, sort_no FROM Board.CATEGORIES ORDER BY sort_no DESC, num;
| num | name | sort_no |
|---|---|---|
| 5 | 모집 | 5 |
| 10 | 기타 | 5 |
| 4 | 질문 | 4 |
| 9 | 오류 | 4 |
sort_no 가 같은 행이 둘씩 있는데, 뒤에 적은
num 이 그 안에서 차례를 정했습니다.
앞에 적은 것이 먼저 적용되고, 같을 때만 뒤엣것을 봅니다.
DESC 는 바로 앞의 열에만 걸립니다. 위 예에서
num 은 오름차순입니다. 둘 다 거꾸로 하려면 각각 적어야 합니다.
여러 열로 묶어 보기
SELECT board_num, sort_no, name FROM Board.CATEGORIES ORDER BY board_num, sort_no;
| board_num | sort_no | name |
|---|---|---|
| 2 | 1 | 잡담 |
| 2 | 2 | 후기 |
| 2 | 3 | 정보 |
| 2 | 4 | 질문 |
| 2 | 5 | 모집 |
| 3 | 1 | 설치 |
| 3 | 2 | 쿼리 |
| 3 | 3 | 성능 |
| 3 | 4 | 오류 |
| 3 | 5 | 기타 |
게시판별로 묶이고 그 안에서 정한 차례대로 나옵니다. 화면의 말머리 목록이 이렇게 만들어집니다.
NULL 은 어디에 놓이나
SQL Server 는 NULL 을 가장 작은 값으로 봅니다. 그래서 오름차순이면 맨 앞에, 내림차순이면 맨 뒤에 놓입니다.
SELECT TOP 6 num, nickname, email FROM Member.USERS ORDER BY email, num;
| num | nickname | |
|---|---|---|
| 5 | 최지우 | NULL |
| 9 | 장미래 | NULL |
| 14 | 신우진 | NULL |
| 19 | 구하람 | NULL |
| 20 | 안지호 | ahn@example.com |
| 17 | 백나윤 | baek@example.com |
전자 메일이 없는 넷이 먼저 나왔습니다. 값이 있는 행을 먼저 보고 싶다면 정렬 열을 하나 더 두어야 합니다.
ORDER BY CASE WHEN email IS NULL THEN 1 ELSE 0 END, email
CASE 는 2.8 에서 다룹니다. 여기서는
정렬 기준을 계산해서 만들 수도 있다는 것만 봐 두십시오.
별칭으로 정렬하기
SELECT 에서 붙인 이름을 ORDER BY 에
그대로 사용할 수 있습니다. 계산한 열을 정렬할 때 편합니다.
SELECT TOP 3 title, hit_count * 2 AS 두배 FROM Board.POSTS ORDER BY 두배 DESC;
| title | 두배 |
|---|---|
| 윈도 함수 질문드립니다 | 996 |
| 외래 키 실무에서 겪은 일 | 994 |
| 실행 계획 초보자가 자주 하는 실수 | 990 |
별칭을 사용할 수 있는 절은 ORDER BY 뿐입니다.
WHERE 에서는 사용할 수 없습니다 — 실행되는 차례가 달라서인데,
그 차례는 2.3 에서 다룹니다.
열 번호로 ORDER BY 2 DESC 처럼 적을 수도 있지만
권하지 않습니다. 나중에 SELECT 에 열을
끼워 넣으면 정렬 기준이 조용히 다른 열로 옮겨 갑니다.
앞에서 몇 개만 — TOP
TOP 은 정렬한 결과에서 앞의 몇 행만 남깁니다.
동점이 있으면 그중 일부만 나오고 나머지는 잘립니다.
SELECT TOP 4 num, hit_count, title FROM Board.POSTS WHERE hit_count BETWEEN 194 AND 198 ORDER BY hit_count DESC;
| num | hit_count | title |
|---|---|---|
| 370 | 198 | [답글] 저장 프로시저 실무에서 겪은 일 |
| 314 | 198 | 윈도 함수 속도가 느립니다 |
| 171 | 197 | 외래 키 문서를 읽어도 모르겠습니다 |
| 28 | 196 | 임시 테이블 정리해 봤습니다 |
조회수 196 인 글이 둘인데 하나만 나왔습니다. 넷째 자리에서 끊겼기 때문입니다.
동점을 함께 내려면 WITH TIES 를 붙입니다.
SELECT TOP 4 WITH TIES num, hit_count, title FROM Board.POSTS WHERE hit_count BETWEEN 194 AND 198 ORDER BY hit_count DESC;
| num | hit_count | title |
|---|---|---|
| 314 | 198 | 윈도 함수 속도가 느립니다 |
| 370 | 198 | [답글] 저장 프로시저 실무에서 겪은 일 |
| 171 | 197 | 외래 키 문서를 읽어도 모르겠습니다 |
| 28 | 196 | 임시 테이블 정리해 봤습니다 |
| 390 | 196 | [답글] 정규화 개념이 헷갈립니다 |
4를 적었는데 5행이 나왔습니다. 마지막 값과 같은 행을 모두 데려오기 때문입니다. 순위표처럼 "공동 4위" 를 함께 보여야 하는 자리에 사용합니다.
두 결과에서 198 의 차례가 다릅니다
위 두 결과를 나란히 보십시오. 조회수 198 인 두 행의 차례가 서로 뒤바뀌어 있습니다.
| 문장 | 198 인 두 행의 차례 |
|---|---|
| TOP 4 | 370 → 314 |
| TOP 4 WITH TIES | 314 → 370 |
ORDER BY 에 hit_count 만 적었으니
같은 값 안에서의 차례는 정해진 것이 없습니다. 데이터베이스가 그때
선택한 읽는 방법에 따라 달라집니다.
차례를 못 박으려면 정렬 열을 하나 더 두십시오.
ORDER BY hit_count DESC, num 처럼 값이 겹치지 않는 열을
뒤에 붙이면 언제 돌려도 같은 결과가 나옵니다. 페이징에서 특히 중요합니다 — 차례가
흔들리면 어떤 글은 두 쪽에 나오고 어떤 글은 어느 쪽에도 안 나옵니다.
가운데부터 몇 개 — OFFSET FETCH
TOP 은 앞에서만 잘라 냅니다. 3쪽을 보여 주려면 앞의 10건을
건너뛰고 다음 5건을 가져와야 하는데, 그때 사용합니다.
SELECT num, title FROM Board.POSTS ORDER BY num OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY;
| num | title |
|---|---|
| 11 | 외래 키 질문드립니다 |
| 12 | 정규화 질문드립니다 |
| 13 | 집계 함수 질문드립니다 |
| 14 | 윈도 함수 질문드립니다 |
| 15 | CTE 질문드립니다 |
OFFSET 은 건너뛸 행 수, FETCH NEXT 는
가져올 행 수입니다. 한 쪽에 5건이면 3쪽은 (3 - 1) * 5 = 10 을
건너뜁니다.
ORDER BY 없이는 사용할 수 없습니다. 차례가
정해져야 몇 번째부터인지도 뜻이 생기기 때문입니다.
글이 수십만 건이 되면 이 방식이 느려집니다. 건너뛸 행을 세느라 앞부분을 모두 읽기 때문인데, 그 대안은 5.6 에서 다룹니다.
게시판 목록은 이렇게 정렬합니다
1.2 에서 본 계층 열이 여기서 쓰입니다. 덩어리를 최신순으로 놓고, 덩어리 안에서는 답글 차례대로 냅니다.
SELECT TOP 6 group_num, depth, sort_no, title FROM Board.POSTS WHERE board_num = 2 ORDER BY group_num DESC, sort_no;
| group_num | depth | sort_no | title |
|---|---|---|---|
| 346 | 0 | 0 | 저장 프로시저 예제 모음 |
| 345 | 0 | 0 | 실행 계획 예제 모음 |
| 345 | 1 | 1 | [답글] 실행 계획 예제 모음 |
| 345 | 2 | 2 | [답글] [답글] 실행 계획 예제 모음 |
| 344 | 0 | 0 | 백업 예제 모음 |
| 343 | 0 | 0 | 페이징 예제 모음 |
답글이 원글 바로 아래 붙어 나옵니다. 정렬 열 두 개로 계층이 표현됐습니다 — 부모를 따라 거슬러 올라가지 않아도 됩니다. 이 방식이 무엇을 얻고 무엇을 포기했는지는 3.9 에서 다른 방식들과 비교합니다.
직접 해보기
가입일이 늦은 회원부터 다섯 명을 닉네임과 함께 보세요.
질문답변 게시판(board_num = 3)의 글을 번호가 큰 것부터 한 쪽에 10건씩 나눌 때, 2쪽에 나올 글을 가져와 보세요.
ORDER BY를 적지 않으면 차례가 정해져 있지 않습니다. 지금 맞아 보여도 언제든 바뀝니다.- 앞에 적은 열이 먼저 적용되고,
DESC는 바로 앞의 열에만 걸립니다. - NULL 은 가장 작은 값으로 보아 오름차순에서 맨 앞에 놓입니다.
TOP은 동점을 자릅니다. 함께 내려면WITH TIES입니다.- 같은 값 안의 차례를 못 박으려면 겹치지 않는 열을 정렬에 하나 더 붙이십시오.
- 가운데부터 가져올 때는
OFFSET FETCH이고,ORDER BY가 반드시 있어야 합니다.