데이터베이스 개념노트 21편 - Top N 쿼리, ROWNUM, FETCH FIRST
지난 글에서는 행을 유지한 상태에서 순위와 누적값을 계산하는 윈도우 함수(Window Function)에 대해 정리했습니다.
이번 글에서는 SQLD에서 빠뜨리면 아쉬운 핵심 주제인 Top N 쿼리, ROWNUM, FETCH FIRST에 대해 정리해 보겠습니다.
Top N 쿼리는 말 그대로 상위 N개 또는 하위 N개의 데이터를 조회하는 SQL입니다. 예를 들어 주문금액 상위 3건, 최근 가입 회원 10명, 매출 상위 5개 상품을 조회할 때 사용합니다.
1. Top N 쿼리란?
Top N 쿼리는 정렬된 결과 중에서 상위 N개 또는 하위 N개의 행만 조회하는 SQL입니다.
Top N 쿼리는 특정 기준으로 정렬한 뒤, 그중 상위 또는 하위 일부 행만 가져오는 쿼리이다.
예를 들어 다음과 같은 상황에서 Top N 쿼리를 사용할 수 있습니다.
- 주문금액이 가장 큰 주문 3건 조회
- 최근 가입한 회원 10명 조회
- 판매량이 가장 높은 상품 5개 조회
- 카테고리별 매출 상위 3개 상품 조회
- 게시글 목록에서 11번부터 20번까지 조회
2. 예제 테이블
이번 글에서는 다음 주문 테이블을 기준으로 예제를 살펴보겠습니다.
| 주문번호 | 회원명 | 카테고리 | 상품명 | 주문금액 | 주문일자 |
|---|---|---|---|---|---|
| O001 | 김지훈 | 전자기기 | 키보드 | 30000 | 2026-01-10 |
| O002 | 김지훈 | 전자기기 | 마우스 | 15000 | 2026-01-12 |
| O003 | 이수진 | 가구 | 책상 | 120000 | 2026-01-15 |
| O004 | 박민수 | 가구 | 의자 | 80000 | 2026-02-01 |
| O005 | 이수진 | 전자기기 | 모니터 | 250000 | 2026-02-05 |
| O006 | 최유진 | 전자기기 | 노트북 | 900000 | 2026-02-10 |
3. Top N 쿼리의 기본 흐름
Top N 쿼리는 보통 다음 순서로 생각하면 됩니다.
- 어떤 기준으로 정렬할지 정한다.
- 상위 몇 개를 가져올지 정한다.
- 정렬 결과에서 원하는 개수만 조회한다.
예를 들어 주문금액 상위 3건을 조회하려면 다음 흐름이 됩니다.
1. 주문금액 기준 내림차순 정렬
2. 정렬된 결과에서 앞의 3건만 조회
4. ORDER BY만 사용한 경우
주문금액이 큰 순서대로 전체 주문을 조회하려면 ORDER BY를 사용합니다.
SELECT 주문번호,
회원명,
상품명,
주문금액
FROM 주문
ORDER BY 주문금액 DESC;
위 SQL은 주문금액이 큰 순서대로 전체 데이터를 정렬합니다. 하지만 상위 3건만 가져오지는 않습니다.
상위 3건만 조회하려면 ROWNUM, FETCH FIRST, 또는 ROW_NUMBER 같은 방법을 함께 사용해야 합니다.
5. ROWNUM이란?
ROWNUM은 Oracle에서 조회 결과에 순서대로 부여되는 가상의 행 번호입니다.
ROWNUM은 Oracle에서 SELECT 결과가 반환될 때 각 행에 임시로 붙는 번호이다.
간단히 말하면 조회되는 행에 1, 2, 3, 4처럼 번호를 붙여주는 가상 컬럼입니다.
SELECT ROWNUM,
주문번호,
회원명,
주문금액
FROM 주문;
위 SQL은 주문 테이블의 각 행에 ROWNUM을 함께 출력합니다.
6. ROWNUM으로 상위 N건 조회하기
가장 단순한 형태의 Top N 쿼리는 ROWNUM을 사용해 앞의 N건만 조회하는 방식입니다.
SELECT 주문번호,
회원명,
상품명,
주문금액
FROM 주문
WHERE ROWNUM <= 3;
위 SQL은 주문 테이블에서 먼저 조회되는 3건만 가져옵니다.
하지만 이 방식은 정렬 기준이 없습니다. 따라서 주문금액 상위 3건을 의미하지는 않습니다. 단순히 먼저 읽힌 3건일 뿐입니다.
7. 잘못된 Top N 예시
주문금액 상위 3건을 조회하려고 다음과 같이 작성하면 문제가 될 수 있습니다.
SELECT 주문번호,
회원명,
상품명,
주문금액
FROM 주문
WHERE ROWNUM <= 3
ORDER BY 주문금액 DESC;
겉으로 보면 주문금액 기준으로 정렬한 뒤 3건을 가져오는 것처럼 보입니다. 하지만 실제로는 그렇지 않을 수 있습니다.
Oracle에서 ROWNUM은 ORDER BY보다 먼저 적용될 수 있습니다. 즉, 먼저 3건을 가져온 뒤 그 3건만 주문금액 기준으로 정렬하는 결과가 될 수 있습니다.
ROWNUM을 이용한 Top N 쿼리에서는 정렬을 먼저 하고, 그 결과에 ROWNUM 조건을 적용해야 합니다.
8. 올바른 ROWNUM Top N 쿼리
주문금액 상위 3건을 올바르게 조회하려면 인라인 뷰를 사용합니다.
SELECT *
FROM (
SELECT 주문번호,
회원명,
상품명,
주문금액
FROM 주문
ORDER BY 주문금액 DESC
)
WHERE ROWNUM <= 3;
이 SQL은 다음 순서로 이해하면 됩니다.
- 인라인 뷰 안에서 주문금액 기준으로 먼저 정렬한다.
- 정렬된 결과에서 바깥 쿼리가 ROWNUM <= 3 조건을 적용한다.
- 최종적으로 주문금액 상위 3건만 조회한다.
Top N 쿼리에서 가장 중요한 패턴 중 하나입니다.
9. 인라인 뷰란?
인라인 뷰는 FROM절 안에 작성하는 서브쿼리입니다.
FROM (
SELECT ...
FROM ...
ORDER BY ...
)
Top N 쿼리에서는 인라인 뷰를 사용해 먼저 정렬된 결과를 만들고, 바깥쪽 SELECT에서 ROWNUM 조건을 적용합니다.
즉, 인라인 뷰는 Top N 쿼리에서 정렬 순서를 보장하기 위해 자주 사용됩니다.
10. 하위 N건 조회하기
하위 N건을 조회할 때도 같은 방식으로 작성할 수 있습니다. 주문금액이 낮은 순서로 3건을 조회해 보겠습니다.
SELECT *
FROM (
SELECT 주문번호,
회원명,
상품명,
주문금액
FROM 주문
ORDER BY 주문금액 ASC
)
WHERE ROWNUM <= 3;
ORDER BY 주문금액 ASC를 사용했기 때문에 주문금액이 낮은 3건이 조회됩니다.
11. ROWNUM > 3이 안 되는 이유
ROWNUM을 처음 공부할 때 자주 헷갈리는 부분이 있습니다. 다음 SQL은 원하는 결과가 나오지 않을 수 있습니다.
SELECT 주문번호,
회원명,
주문금액
FROM 주문
WHERE ROWNUM > 3;
ROWNUM은 행이 결과로 선택될 때 1부터 부여됩니다. 그런데 첫 번째 행이 ROWNUM = 1인 상태에서 ROWNUM > 3 조건을 만족하지 못하면 결과에 포함되지 않습니다. 다음 행도 다시 첫 번째 후보 행처럼 평가되므로 계속 조건을 만족하지 못합니다.
따라서 ROWNUM > 3 같은 조건은 단독으로 사용하면 결과가 나오지 않을 수 있습니다.
12. 특정 구간 조회하기
게시판 페이징처럼 4번째부터 10번째까지 조회하고 싶다면 ROWNUM에 별칭을 붙인 뒤 바깥쪽에서 필터링합니다.
SELECT *
FROM (
SELECT ROWNUM AS RN,
A.*
FROM (
SELECT 주문번호,
회원명,
상품명,
주문금액
FROM 주문
ORDER BY 주문금액 DESC
) A
WHERE ROWNUM <= 10
)
WHERE RN >= 4;
이 SQL은 다음 순서로 동작한다고 이해하면 됩니다.
- 주문금액 기준으로 정렬한다.
- 정렬된 결과에서 10번째까지 ROWNUM을 붙인다.
- 그중 RN이 4 이상인 행만 조회한다.
즉, 최종적으로 4번째부터 10번째까지의 행을 조회합니다.
13. FETCH FIRST란?
FETCH FIRST는 정렬된 결과에서 앞의 N건만 가져오는 문법입니다. ROWNUM보다 직관적으로 Top N 쿼리를 작성할 수 있습니다.
FETCH FIRST는 ORDER BY 결과 중 상위 N개의 행만 반환하는 문법이다.
주문금액 상위 3건을 조회하려면 다음과 같이 작성할 수 있습니다.
SELECT 주문번호,
회원명,
상품명,
주문금액
FROM 주문
ORDER BY 주문금액 DESC
FETCH FIRST 3 ROWS ONLY;
위 SQL은 주문금액 기준으로 내림차순 정렬한 뒤, 앞의 3건만 조회합니다.
14. FETCH FIRST 문법 구조
FETCH FIRST의 기본 구조는 다음과 같습니다.
SELECT 컬럼명
FROM 테이블명
ORDER BY 정렬기준
FETCH FIRST N ROWS ONLY;
| 구문 | 의미 |
|---|---|
| ORDER BY | 어떤 기준으로 정렬할지 지정 |
| FETCH FIRST N ROWS ONLY | 정렬 결과 중 앞의 N건만 조회 |
FETCH FIRST는 ROWNUM보다 의미가 명확해서 Top N 쿼리를 읽기 쉽습니다.
15. FETCH FIRST와 OFFSET
OFFSET은 앞의 몇 개 행을 건너뛸지 지정하는 문법입니다. 게시판 페이징 처리에서 자주 사용됩니다.
SELECT 주문번호,
회원명,
상품명,
주문금액
FROM 주문
ORDER BY 주문금액 DESC
OFFSET 3 ROWS
FETCH NEXT 3 ROWS ONLY;
위 SQL은 주문금액 기준으로 정렬한 뒤, 앞의 3건을 건너뛰고 다음 3건을 조회합니다.
| 구문 | 의미 |
|---|---|
| OFFSET 3 ROWS | 앞의 3개 행을 건너뜀 |
| FETCH NEXT 3 ROWS ONLY | 그 다음 3개 행을 조회 |
16. WITH TIES
WITH TIES는 마지막 순위와 같은 값을 가진 행까지 함께 조회할 때 사용합니다.
예를 들어 주문금액 상위 3건을 조회하는데, 3번째 주문과 같은 금액을 가진 주문이 더 있다면 함께 조회하고 싶을 수 있습니다.
SELECT 주문번호,
회원명,
상품명,
주문금액
FROM 주문
ORDER BY 주문금액 DESC
FETCH FIRST 3 ROWS WITH TIES;
WITH TIES를 사용하면 경계에 걸린 동일한 정렬 값의 행까지 포함될 수 있습니다.
즉, 정확히 3건만 나오는 것이 아니라 동점이 있으면 3건보다 더 많이 나올 수 있습니다.
17. ROWNUM과 FETCH FIRST 비교
| 구분 | ROWNUM | FETCH FIRST |
|---|---|---|
| 작성 방식 | 인라인 뷰와 함께 사용하는 경우가 많음 | ORDER BY 뒤에 직접 작성 |
| 가독성 | 처리 순서를 이해해야 함 | 상위 N건 의미가 직관적 |
| 정렬 후 Top N | 인라인 뷰 필요 | ORDER BY와 함께 사용 |
| 페이징 | 중첩 서브쿼리 필요 | OFFSET과 함께 사용 가능 |
18. ROW_NUMBER를 이용한 Top N
윈도우 함수인 ROW_NUMBER를 이용해 Top N 쿼리를 작성할 수도 있습니다.
SELECT *
FROM (
SELECT 주문번호,
회원명,
상품명,
주문금액,
ROW_NUMBER() OVER (ORDER BY 주문금액 DESC) AS RN
FROM 주문
)
WHERE RN <= 3;
위 SQL은 주문금액 기준으로 순번을 부여한 뒤, RN이 3 이하인 행만 조회합니다.
ROW_NUMBER 방식은 특히 그룹별 Top N을 구할 때 유용합니다.
19. 그룹별 Top N 쿼리
전체 상위 N건이 아니라 카테고리별 상위 N건을 조회하고 싶을 때가 있습니다. 예를 들어 카테고리별 주문금액 상위 2건을 조회해 보겠습니다.
SELECT *
FROM (
SELECT 주문번호,
카테고리,
상품명,
주문금액,
ROW_NUMBER() OVER (
PARTITION BY 카테고리
ORDER BY 주문금액 DESC
) AS RN
FROM 주문
)
WHERE RN <= 2;
이 SQL은 카테고리별로 주문금액 순위를 따로 매기고, 각 카테고리에서 상위 2건만 조회합니다.
PARTITION BY는 카테고리별로 그룹을 나누고, ORDER BY는 각 카테고리 안에서 주문금액 순서를 정합니다.
20. ROW_NUMBER, RANK, DENSE_RANK와 Top N
Top N 쿼리에서 동점 처리를 어떻게 할지에 따라 사용하는 함수가 달라질 수 있습니다.
| 함수 | 동점 처리 | Top N 결과 특징 |
|---|---|---|
| ROW_NUMBER | 동점이어도 서로 다른 번호 부여 | 정확히 N건만 조회하기 쉬움 |
| RANK | 동점이면 같은 순위, 다음 순위 건너뜀 | 동점 포함 시 N건보다 많아질 수 있음 |
| DENSE_RANK | 동점이면 같은 순위, 다음 순위 건너뛰지 않음 | 순위 그룹 기준으로 조회할 때 유용 |
예를 들어 3등까지 조회할 때 3등이 여러 명이면 RANK나 DENSE_RANK를 사용한 결과는 3건보다 많아질 수 있습니다.
21. RANK를 이용한 동점 포함 Top N
동점 순위까지 포함해서 상위 3등을 조회하려면 RANK를 사용할 수 있습니다.
SELECT *
FROM (
SELECT 주문번호,
회원명,
상품명,
주문금액,
RANK() OVER (ORDER BY 주문금액 DESC) AS 순위
FROM 주문
)
WHERE 순위 <= 3;
이 SQL은 주문금액 기준으로 순위를 매긴 뒤 3등까지 조회합니다. 동점자가 있다면 3건보다 더 많이 조회될 수 있습니다.
22. Top N 쿼리와 NULL 정렬
Top N 쿼리는 정렬 기준이 중요합니다. 정렬 컬럼에 NULL이 있을 경우 NULL이 어디에 위치하는지도 결과에 영향을 줄 수 있습니다.
Oracle에서는 ORDER BY에서 NULLS FIRST, NULLS LAST를 사용할 수 있습니다.
SELECT 주문번호,
회원명,
주문금액
FROM 주문
ORDER BY 주문금액 DESC NULLS LAST
FETCH FIRST 3 ROWS ONLY;
위 SQL은 주문금액이 큰 순서로 정렬하되, NULL 값은 마지막에 배치합니다.
| 구문 | 의미 |
|---|---|
| NULLS FIRST | NULL 값을 먼저 정렬 |
| NULLS LAST | NULL 값을 나중에 정렬 |
23. Top N 쿼리와 ORDER BY 중요성
Top N 쿼리에서 ORDER BY가 없으면 상위 N건의 의미가 불명확해집니다.
SELECT *
FROM 주문
FETCH FIRST 3 ROWS ONLY;
위 SQL은 3건을 가져오기는 하지만 어떤 기준의 상위 3건인지는 알 수 없습니다. 따라서 Top N 쿼리에서는 ORDER BY를 함께 사용하는 것이 중요합니다.
Top N 쿼리는 반드시 어떤 기준의 Top N인지 ORDER BY로 명확히 하는 것이 좋습니다.
24. Top N 쿼리와 인덱스
Top N 쿼리는 정렬이 중요하기 때문에 인덱스와도 관련이 있습니다. 정렬 기준 컬럼에 적절한 인덱스가 있으면 Top N 조회 성능에 도움이 될 수 있습니다.
CREATE INDEX IDX_주문_주문금액
ON 주문(주문금액);
주문금액 기준으로 자주 Top N 조회를 한다면 주문금액 컬럼의 인덱스를 검토할 수 있습니다.
다만 인덱스는 SELECT 성능에는 도움이 될 수 있지만, INSERT, UPDATE, DELETE 작업에는 부담을 줄 수 있으므로 신중하게 설계해야 합니다.
25. Top N 쿼리에서 자주 하는 실수
| 실수 | 주의할 점 |
|---|---|
| ROWNUM 조건을 ORDER BY와 같은 단계에서 사용 | 먼저 일부 행을 가져온 뒤 정렬될 수 있으므로 인라인 뷰 사용 |
| ROWNUM > N 조건을 단독으로 사용 | 원하는 결과가 나오지 않을 수 있음 |
| ORDER BY 없이 FETCH FIRST 사용 | 어떤 기준의 Top N인지 불명확함 |
| ROW_NUMBER와 RANK의 동점 처리를 혼동 | ROW_NUMBER는 고유 번호, RANK는 동점 같은 순위 |
| 그룹별 Top N에 단순 ROWNUM 사용 | 그룹별 Top N은 ROW_NUMBER와 PARTITION BY 활용 |
26. SQLD 관점에서 꼭 기억할 내용
이번 글에서 SQLD 공부를 위해 꼭 기억해야 할 내용은 다음과 같습니다.
- Top N 쿼리는 정렬 결과 중 상위 또는 하위 N건을 조회하는 쿼리이다.
- ROWNUM은 Oracle에서 조회 결과에 임시로 부여되는 행 번호이다.
- ROWNUM은 ORDER BY보다 먼저 적용될 수 있으므로 주의해야 한다.
- 정렬 후 ROWNUM 조건을 적용하려면 인라인 뷰를 사용한다.
- ROWNUM > N 조건은 단독으로 사용하면 원하는 결과가 나오지 않을 수 있다.
- FETCH FIRST N ROWS ONLY는 상위 N건을 직관적으로 조회하는 문법이다.
- OFFSET은 앞의 행을 건너뛸 때 사용한다.
- WITH TIES는 마지막 순위와 같은 값을 가진 행까지 포함한다.
- 그룹별 Top N은 ROW_NUMBER와 PARTITION BY를 활용할 수 있다.
- Top N 쿼리에서는 ORDER BY 기준이 매우 중요하다.
27. 핵심 요약
| 개념 | 핵심 내용 |
|---|---|
| Top N 쿼리 | 정렬 결과 중 상위 또는 하위 N건을 조회하는 쿼리 |
| ROWNUM | Oracle에서 조회 결과에 부여되는 가상의 행 번호 |
| 인라인 뷰 | FROM절 안의 서브쿼리로, 정렬 후 ROWNUM 적용 시 사용 |
| FETCH FIRST | 정렬 결과에서 앞의 N건만 조회하는 문법 |
| OFFSET | 앞의 행을 건너뛰고 다음 행부터 조회하는 문법 |
| WITH TIES | 마지막 순위와 같은 정렬 값을 가진 행까지 포함 |
| ROW_NUMBER | 각 행에 고유 순번을 부여하여 Top N 조회 가능 |
| RANK | 동점 순위를 포함한 Top N 조회에 활용 가능 |
28. 마무리
이번 글에서는 SQLD에서 빠뜨리면 아쉬운 Top N 쿼리, ROWNUM, FETCH FIRST에 대해 정리했습니다.
Top N 쿼리는 단순히 몇 개의 행을 가져오는 것이 아니라, 반드시 어떤 기준으로 정렬한 뒤 상위 또는 하위 데이터를 가져오는 방식으로 이해해야 합니다.
ROWNUM을 사용할 때는 ORDER BY와의 처리 순서를 주의해야 하며, 정렬 후 Top N을 구하려면 인라인 뷰를 사용하는 패턴을 기억해야 합니다. FETCH FIRST는 더 직관적으로 상위 N건을 조회할 수 있는 문법입니다.
또한 그룹별 Top N을 구할 때는 ROW_NUMBER, RANK, DENSE_RANK 같은 윈도우 함수와 PARTITION BY를 함께 활용할 수 있습니다.
다음 글에서는 계층형 질의 START WITH, CONNECT BY PRIOR에 대해 정리해 보겠습니다.

'Computer Science > Database' 카테고리의 다른 글
| [DB]데이터베이스 개념노트 23편 - PIVOT, UNPIVOT (0) | 2026.05.27 |
|---|---|
| [DB] 데이터베이스 개념노트 22편 - 계층형 질의 START WITH, CONNECT BY PRIOR (0) | 2026.05.27 |
| [DB] 데이터베이스 개념노트 20편 - 윈도우 함수(Window Function) (0) | 2026.05.27 |
| [DB] 데이터베이스 개념노트 19편 - 집합 연산자 UNION, UNION ALL, INTERSECT, MINUS (0) | 2026.05.26 |
| [DB] 데이터베이스 개념노트 18편 - 서브쿼리(Subquery) (0) | 2026.05.26 |