ORDER BY와 Top-N
다중 정렬 기준·NULL 배치·ROW_NUMBER와 FETCH를 이용한 안정적인 Top-N을 이해한다.
핵심 요약
다중 정렬 기준·NULL 배치·ROW_NUMBER와 FETCH를 이용한 안정적인 Top-N을 이해한다.
핵심 질문
- ORDER BY와 Top-N에서 반드시 구분해야 할 개념과 결과 규칙은 무엇인가?
- 0건·1건·여러 건과 NULL·동점·중복 데이터에서 결과가 어떻게 달라지는가?
- 비슷해 보이는 문법과 결과가 같아지는 조건, 달라지는 조건은 무엇인가?
- 작은 샘플 데이터를 이용해 결과를 실수 없이 예측하는 순서는 무엇인가?
학습 목표
- ASC·DESC와 NULL 정렬 위치를 명시한다.
- Top-N과 페이지 조회에 결정적인 정렬 기준이 필요한 이유를 설명한다.
개념 지도
결과 행 생성 → 정렬 기준·방향·NULL 위치 → 동점 Tie-breaker → 행 제한
핵심 내용
관계형 결과는 ORDER BY가 없으면 순서가 보장되지 않는다. 여러 기준을 왼쪽부터 적용하며 각 기준마다 ASC·DESC를 지정할 수 있다.
SELECT empno, ename, sal
FROM emp
ORDER BY sal DESC, empno ASC;
동점이 존재할 수 있는 sal만 정렬하면 실행마다 동점 행 순서가 달라질 수 있다. PK 같은 유일한 컬럼을 마지막 정렬 기준으로 추가하면 안정적이다. Oracle은 NULLS FIRST, NULLS LAST로 NULL 위치를 명시할 수 있다.
상위 N건은 최신 문법의 FETCH FIRST 또는 분석 함수로 표현한다.
SELECT empno, sal
FROM emp
ORDER BY sal DESC, empno
FETCH FIRST 5 ROWS ONLY;
ROWNUM을 사용한다면 정렬보다 먼저 부여될 수 있으므로 정렬한 인라인 뷰 바깥에서 제한하는 구조를 이해해야 한다.
흔한 오해와 주의점
- ORDER BY의 숫자는 SELECT 목록의 위치를 뜻할 수 있지만 유지보수성이 낮다.
- Top-N에서 동점 처리 여부와 정확히 N행 반환 여부를 구분한다.
- 페이지 번호가 깊어질수록 OFFSET 방식은 앞 행을 계속 처리해야 할 수 있다.
문항 풀이 보강: Top-N 문법별 실행 순서
Oracle ROWNUM
ROWNUM은 같은 쿼리 블록의 ORDER BY보다 먼저 붙을 수 있다. 먼저 정렬한 뒤 바깥에서 제한한다.
SELECT *
FROM (
SELECT team_name, wins
FROM team_score
ORDER BY wins DESC
)
WHERE ROWNUM <= 3;
동점까지 포함하려면 RANK나 DENSE_RANK, 또는 DBMS가 지원하는 FETCH FIRST 3 ROWS WITH TIES를 사용한다.
SELECT *
FROM team_score
ORDER BY wins DESC
FETCH FIRST 3 ROWS WITH TIES;
SQL Server의 TOP (3) WITH TIES도 ORDER BY의 마지막 경계값과 같은 행을 함께 반환한다.
순위 함수 선택
- 정확히 N행이 필요하면 결정적 ORDER BY와
ROW_NUMBER. - 동점 다음 순위를 건너뛰는 경기 순위면
RANK. - 동점 뒤에도 연속 순위면
DENSE_RANK.
정렬 기준이 유일하지 않으면 같은 점수의 행 순서는 달라질 수 있으므로 정확히 N행을 요구할 때는 PK 같은 보조 정렬키를 추가한다.
결정적인 정렬 만들기
SELECT order_id, created_at, amount
FROM orders
ORDER BY created_at DESC, order_id DESC;
created_at만 정렬하면 같은 시각의 행 순서는 결정되지 않는다. PK인 order_id를 Tie-breaker로 추가해야 반복 실행과 Pagination에서 안정적인 순서를 얻는다.
FETCH FIRST와 WITH TIES
-- 정확히 10행 이하
FETCH FIRST 10 ROWS ONLY
-- 10번째 행과 정렬값이 같은 동점까지 포함
FETCH FIRST 10 ROWS WITH TIES
WITH TIES는 반환 행 수가 10보다 많을 수 있다. 동점 기준은 전체 ORDER BY 표현식이므로 PK까지 포함하면 사실상 Tie가 없어질 수 있다.
ROWNUM의 올바른 위치
SELECT *
FROM (
SELECT order_id, amount
FROM orders
ORDER BY amount DESC, order_id
)
WHERE ROWNUM <= 10;
ROWNUM을 정렬과 같은 Query Block에 두면 먼저 선택된 행을 정렬할 수 있다. Oracle 12c 이상의 Row Limiting Clause를 사용하더라도 동점 행의 순서를 결정할 추가 정렬 기준이 필요하다.
NULL 정렬
Oracle의 기본 NULL 위치는 정렬 방향에 따라 달라질 수 있으므로 업무 요구가 있으면 NULLS FIRST 또는 NULLS LAST를 명시한다. SQL 결과 순서는 ORDER BY 없이는 보장되지 않는다.
결과를 검증하는 순서
- 각 Query Block이 만드는 한 행의 의미를 먼저 적습니다.
- 조건을 적용하기 전 원본 행과 적용 후 남는 행을 작은 표로 그립니다.
- NULL 비교가
TRUE,FALSE,UNKNOWN중 무엇인지 구분합니다. - 중복 제거, 그룹화, 정렬과 행 제한이 적용되는 순서를 확인합니다.
- 데이터가 0건·1건·여러 건일 때도 같은 규칙이 성립하는지 검증합니다.
실무와 시험에서 함께 확인할 항목
ORDER BY가 없다면 결과 순서를 가정하지 않습니다.- 문자열·숫자·날짜 비교에서는 데이터 타입과 명시적 형변환을 확인합니다.
- 같은 결과처럼 보이는 SQL도 NULL과 중복이 있을 때 달라질 수 있습니다.
- 문법을 외우기 전에 샘플 데이터 3~5행으로 결과를 직접 계산합니다.
마지막 점검
- 작성 순서가 아니라 SQL의 논리적 처리 순서로 결과를 계산합니다.
- NULL을 0이나 빈 값과 같은 것으로 취급하지 않습니다.
ORDER BY가 없는 결과 순서와 DISTINCT 없는 중복 제거를 가정하지 않습니다.- 비슷한 문법은 0건·다건·NULL 데이터를 넣어 결과가 정말 같은지 확인합니다.
복습 문제
- 동점이 있는 결과에 PK 정렬을 추가하는 이유는?
- ROWNUM과 ORDER BY를 같은 단계에서 단순 결합하면 생길 수 있는 문제는?
- 샘플 데이터 3행으로 결과를 직접 계산할 수 있는가?
- NULL이 포함될 때 결과가 달라지는 지점은 어디인가?