SQL ORDER BY 절
더 많은 작업
- 상위 문서: SQL
- ORDER BY Clause
- 조회 결과 행을 지정한 기준에 따라 정렬하는 SQL 절
ORDER BY 절은 SELECT 문이 돌려주는 결과 행의 순서를 정한다. ORDER BY가 없으면 결과 순서는 보장되지 않는다. 테이블에 넣은 순서나 인덱스 순서로 나오는 것처럼 보여도 실행 계획이 바뀌면 달라질 수 있다. ORDER BY는 SELECT 문의 맨 끝에 쓰며, 논리적 처리 순서에서도 가장 마지막에 실행된다.
B_EMP
| 사원번호 | 이름 | 부서번호 | 관리자번호 | 급여 | 입사일 | 커미션 |
|---|---|---|---|---|---|---|
| 1001 | 김대표 | 10 | NULL | 9000 | 2015-01-05 | NULL |
| 1002 | 이부장 | 20 | 1001 | 6000 | 2017-03-15 | NULL |
| 1003 | 박과장 | 20 | 1002 | 4500 | 2019-07-01 | NULL |
| 1004 | 최대리 | 30 | 1001 | 4000 | 2020-11-20 | 500 |
| 1005 | 정사원 | 30 | 1004 | 3000 | 2022-02-28 | 0 |
| 1006 | 한신입 | NULL | 1002 | 2500 | 2024-08-01 | NULL |
SELECT 칼럼 목록
FROM 테이블
[WHERE 조건]
[GROUP BY 칼럼] [HAVING 조건]
ORDER BY 기준1 [ASC | DESC] [NULLS FIRST | NULLS LAST], 기준2 [ASC | DESC] ...;
- ASC는 오름차순(작은 값부터)이며 기본값이다. DESC는 내림차순이다.
- 숫자는 작은 수, 날짜는 이른 날짜, 문자는 사전 순으로 앞선 값이 오름차순에서 먼저 나온다.
| 방법 | 예 | 설명 |
|---|---|---|
| 칼럼명 | ORDER BY 급여 DESC |
가장 일반적인 방법 |
| 별칭 | SELECT 급여*12 AS 연봉 ... ORDER BY 연봉 DESC |
SELECT 절에서 붙인 별칭을 쓸 수 있다. |
| 위치 번호 | SELECT 이름, 급여 ... ORDER BY 2 DESC, 1 |
SELECT 목록의 순번. 목록에 없는 번호를 쓰면 ORA-01785 오류 |
| 표현식 | ORDER BY CASE 부서번호 WHEN 30 THEN 1 ... END |
함수나 CASE 등의 계산 결과로 정렬 |
- 칼럼명, 별칭, 위치 번호를 한 ORDER BY 안에 섞어 쓸 수 있다.
첫 번째 기준 값이 같은 행끼리 두 번째 기준으로 정렬한다. ASC와 DESC는 칼럼마다 따로 지정한다.
SELECT 이름, 부서번호, 급여 FROM B_EMP
ORDER BY 부서번호, 급여 DESC; -- 부서번호는 ASC(생략), 급여는 DESC
| 이름 | 부서번호 | 급여 |
|---|---|---|
| 김대표 | 10 | 9000 |
| 이부장 | 20 | 6000 |
| 박과장 | 20 | 4500 |
| 최대리 | 30 | 4000 |
| 정사원 | 30 | 3000 |
| 한신입 | NULL | 2500 |
Oracle은 정렬할 때 NULL을 가장 큰 값으로 취급한다. 그래서 ASC에서는 맨 뒤, DESC에서는 맨 앞에 온다. SQL Server는 반대로 NULL을 가장 작은 값으로 취급한다.
| DBMS | ASC | DESC |
|---|---|---|
| Oracle | NULL이 마지막 | NULL이 처음 |
| SQL Server | NULL이 처음 | NULL이 마지막 |
Oracle은 NULLS FIRST, NULLS LAST로 NULL 위치를 직접 정할 수 있다.
SELECT 이름, 부서번호 FROM B_EMP ORDER BY 부서번호 DESC, 사원번호; -- (1)
SELECT 이름, 부서번호 FROM B_EMP ORDER BY 부서번호 DESC NULLS LAST, 사원번호; -- (2)
SELECT 이름, 부서번호 FROM B_EMP ORDER BY 부서번호 ASC NULLS FIRST, 사원번호; -- (3)
| 순서 | (1) DESC | (2) DESC NULLS LAST | (3) ASC NULLS FIRST |
|---|---|---|---|
| 1 | 한신입(NULL) | 최대리(30) | 한신입(NULL) |
| 2 | 최대리(30) | 정사원(30) | 김대표(10) |
| 3 | 정사원(30) | 이부장(20) | 이부장(20) |
| 4 | 이부장(20) | 박과장(20) | 박과장(20) |
| 5 | 박과장(20) | 김대표(10) | 최대리(30) |
| 6 | 김대표(10) | 한신입(NULL) | 정사원(30) |
- SQL Server는 NULLS FIRST·NULLS LAST 구문이 없어
ORDER BY CASE WHEN 칼럼 IS NULL THEN 1 ELSE 0 END, 칼럼처럼 CASE로 처리한다.
일반 SELECT 문에서는 SELECT 목록에 없는 칼럼으로도 정렬할 수 있다. 그러나 GROUP BY나 DISTINCT를 쓰면 정렬 기준이 제한된다. Oracle에서 실행한 결과는 다음과 같다.
| 쿼리 | 결과 |
|---|---|
SELECT 이름 FROM B_EMP ORDER BY 입사일 DESC |
가능. 한신입, 정사원, 최대리, 박과장, 이부장, 김대표 |
SELECT SUM(급여) AS 합계 FROM B_EMP GROUP BY 부서번호 ORDER BY 부서번호 |
가능. GROUP BY 표현식은 SELECT 목록에 없어도 된다. |
SELECT 부서번호, SUM(급여) FROM B_EMP GROUP BY 부서번호 ORDER BY MAX(급여) DESC |
가능. 집계 함수로 정렬할 수 있다. |
SELECT 부서번호, SUM(급여) FROM B_EMP GROUP BY 부서번호 ORDER BY 급여 |
ORA-00979 오류. 그룹으로 묶인 뒤에는 개별 급여 값이 없다. |
SELECT DISTINCT 부서번호 FROM B_EMP ORDER BY 급여 |
ORA-01791(not a SELECTed expression) 오류. DISTINCT를 쓰면 SELECT 목록에 있는 것으로만 정렬할 수 있다. |
- ORA-00979의 메시지는 버전에 따라 다르다. 19c까지는
not a GROUP BY expression, 23ai 이후는 칼럼이 GROUP BY 절에 있거나 집계 함수에 쓰여야 한다는 문구로 나온다.
논리적 처리 순서는 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY이다. ORDER BY는 SELECT 다음에 처리되므로 SELECT 절의 별칭을 쓸 수 있다. WHERE, GROUP BY, HAVING 절에서는 별칭을 쓸 수 없다(SQL WHERE 절).
SELECT 이름, 급여*12 AS 연봉 FROM B_EMP ORDER BY 연봉 DESC;
-- 김대표 108000, 이부장 72000, 박과장 54000, 최대리 48000, 정사원 36000, 한신입 30000
값의 크기 순서가 아닌 업무상 순서로 정렬하려면 CASE로 순번을 만들어 정렬한다.
SELECT 이름, 부서번호 FROM B_EMP
ORDER BY CASE 부서번호 WHEN 30 THEN 1 WHEN 10 THEN 2 ELSE 3 END, 사원번호;
| 이름 | 부서번호 |
|---|---|
| 최대리 | 30 |
| 정사원 | 30 |
| 김대표 | 10 |
| 이부장 | 20 |
| 박과장 | 20 |
| 한신입 | NULL |
ROWNUM은 WHERE 절에서 행이 걸러질 때 붙고, ORDER BY는 그 뒤에 실행된다. 따라서 같은 쿼리 안에서 ROWNUM으로 자른 뒤 정렬하면 "급여가 낮은 3명"이 아니라 "먼저 읽힌 3명을 급여순으로 정렬한 것"이 된다.
-- (1) 잘못된 방법: 먼저 3행을 고른 뒤 정렬
SELECT 이름, 급여 FROM B_EMP WHERE ROWNUM <= 3 ORDER BY 급여;
-- (2) 올바른 방법: 인라인 뷰에서 정렬한 뒤 3행을 고른다
SELECT 이름, 급여 FROM (SELECT 이름, 급여 FROM B_EMP ORDER BY 급여) WHERE ROWNUM <= 3;
| (1) 결과 | (2) 결과 |
|---|---|
| 박과장 4500 | 한신입 2500 |
| 이부장 6000 | 정사원 3000 |
| 김대표 9000 | 최대리 4000 |
- (1)의 결과는 테이블에서 행을 읽은 순서에 따라 달라질 수 있다. 자세한 내용은 Top N 쿼리에서 다룬다.
UNION, UNION ALL, INTERSECT, MINUS로 여러 SELECT를 합칠 때 ORDER BY는 맨 마지막 SELECT 뒤에 한 번만 쓰며, 전체 결과를 정렬한다(SQL 집합 연산자).
SELECT 이름 AS 성명 FROM B_EMP WHERE 부서번호 = 10
UNION ALL
SELECT 이름 FROM B_EMP WHERE 부서번호 = 30
ORDER BY 1 DESC; -- 최대리, 정사원, 김대표
| 쿼리 | 결과 |
|---|---|
| 첫 SELECT 뒤에 ORDER BY를 쓰고 UNION ALL을 이어 씀 | 구문 오류. 26ai에서는 ORA-03048(UNION이 올 수 없는 위치)로 표시된다. |
ORDER BY 1 DESC 또는 ORDER BY 성명 |
가능. 위치 번호나 첫 번째 SELECT의 칼럼명·별칭을 쓴다. |
두 번째 SELECT의 별칭(ORDER BY 명) |
ORA-00904 오류 |
SELECT 목록에 없는 칼럼(ORDER BY 급여) |
ORA-00904 오류 |
- Oracle은 NULL을 가장 큰 값으로 보아 ASC에서 마지막, DESC에서 처음에 둔다. SQL Server는 반대이다. NULLS FIRST·NULLS LAST로 바꿀 수 있다.
- ORDER BY에는 칼럼명, 별칭, SELECT 목록 위치 번호를 쓸 수 있고 섞어 써도 된다. 목록 범위를 벗어난 번호는 오류이다.
- SELECT 목록에 없는 칼럼으로 정렬: 일반 SELECT는 가능, GROUP BY 쿼리는 GROUP BY 표현식과 집계 함수만, DISTINCT 쿼리는 SELECT 목록 항목만 가능하다.
- ORDER BY는 가장 마지막에 실행되므로 별칭을 쓸 수 있고, ROWNUM 조건보다 늦게 적용된다.
- 집합 연산자를 쓰면 ORDER BY는 맨 끝에 한 번만, 첫 번째 SELECT의 칼럼명·별칭 또는 위치 번호로 쓴다.