TOP-N 쿼리는 결과 집합에서 상위 N개 또는 하위 N개의 행을 조회하는 기법입니다. SQLD 시험에서는 이 개념을 구현하는 다양한 방법(특히 ROWNUM, ROW_NUMBER, FETCH 등)과 각 방법의 특징을 명확히 구분하는 문제가 자주 출제됩니다.
TOP-N 쿼리는 단순히 ORDER BY만으로는 구현할 수 없으며, 순위를 매기는 함수나 가상 컬럼을 활용해야 합니다.
| 구분 | ROWNUM (Oracle) | ROW_NUMBER() (ANSI) | RANK() (ANSI) | DENSE_RANK() (ANSI) |
|---|---|---|---|---|
| 개념 | 쿼리 결과에 부여되는 가상 행 번호 | 윈도우 함수로 순차적인 순위 부여 | 동일 값에 같은 순위를 부여, 다음 순위 건너뜀 | 동일 값에 같은 순위를 부여, 다음 순위 건너뛰지 않음 |
| 특징 | ORDER BY보다 먼저 처리되어 정렬 전의 순위가 매겨짐. | ORDER BY 후 순위가 매겨져 정확한 순서를 부여. | (1, 2, 2, 4) | (1, 2, 2, 3) |
| 주의점 | WHERE ROWNUM > N 조건으로 단독 사용 시 결과 없음. | PARTITION BY와 함께 그룹별 순위 부여 가능. | DENSE_RANK()와 차이점 구분. | RANK()와 차이점 구분. |
ROWNUM을 활용한 TOP-NROWNUM은 ORDER BY보다 먼저 실행되기 때문에, 먼저 정렬을 수행하는 인라인 뷰를 사용해야 원하는 결과를 얻을 수 있습니다.
SELECT employee_name, salary
FROM (
SELECT employee_name, salary
FROM employees
ORDER BY salary DESC
) -- 정렬이 먼저 수행
WHERE ROWNUM <= 3; -- 정렬된 결과에 ROWNUM 부여
FETCH FIRST를 활용한 TOP-N (ANSI 표준)ANSI SQL 2011 표준 문법으로, ORDER BY 절 뒤에 위치하여 직관적입니다.
SELECT employee_name, salary
FROM employees
ORDER BY salary DESC
FETCH FIRST 3 ROWS ONLY;
RANK 윈도우 함수 활용WHERE 절에 윈도우 함수를 직접 사용할 수 없으므로, 인라인 뷰나 **CTE(WITH 절)**를 사용해야 합니다.
SELECT employee_name, salary
FROM (
SELECT employee_name, salary,
RANK() OVER (ORDER BY salary DESC) as rnk
FROM employees
)
WHERE rnk <= 3;
ROWNUM의 치명적 함정: WHERE ROWNUM > 1과 같은 조건은 논리적 오류로 인해 항상 FALSE가 되어 결과를 반환하지 않습니다. ROWNUM은 <=, < 조건에만 정상 작동합니다. (★★★★★)ROWNUM은 ORDER BY보다 먼저 실행됩니다. 따라서 ROWNUM을 사용하여 정확한 순위의 TOP-N을 얻으려면 반드시 ORDER BY가 포함된 서브쿼리를 사용해야 합니다.RANK vs DENSE_RANK: 동점자가 있을 때 순위 건너뜀 여부를 묻는 문제가 자주 나옵니다. RANK는 다음 순위를 건너뛰고, DENSE_RANK는 건너뛰지 않습니다.ROWNUM: **O**racle, **O**rder by **O**utside (정렬을 서브쿼리 안에서!)FETCH: **F**irst **F**ew rows (몇 개만 가져와!)RANK: 동점자를 **R**espect해서 다음 순위를 건너뜀.DENSE_RANK: **D**ense (촘촘하게) 순위를 매겨서 건너뛰지 않음.1. 다음 쿼리의 실행 결과에 대한 설명으로 옳은 것은?
SELECT employee_name, salary
FROM employees
WHERE ROWNUM <= 3
ORDER BY salary DESC;
A. 연봉 순으로 상위 3명의 직원이 정확히 출력된다.
B. 정렬되지 않은 상태에서 3개 행이 선택된 후 정렬되므로, 연봉 순서와 맞지 않을 수 있다.
C. ROWNUM 조건으로 인해 구문 오류가 발생한다.
D. WHERE 조건이 ORDER BY보다 나중에 처리되어 의도한 결과를 얻을 수 있다.
2. ROWNUM > 3과 ROWNUM < 4 중 논리적으로 유효한 WHERE 조건은?
A. ROWNUM > 3
B. ROWNUM < 4
C. 둘 다 유효하다.
D. 둘 다 잘못된 조건이다.
3. 다음 중 연봉이 동일한 직원에게 같은 순위를 부여하고, 다음 순위를 건너뛰지 않는 함수는?
A. ROW_NUMBER()
B. RANK()
C. DENSE_RANK()
D. NTILE()
ROWNUM은 WHERE 절보다 먼저 실행됩니다. 따라서 정렬되지 않은 상태에서 무작위로 3개의 행에 ROWNUM <= 3 조건이 적용되고, 그 후에 정렬되기 때문에 연봉 순서와 맞지 않을 수 있습니다.ROWNUM은 1부터 순차적으로 부여되므로 ROWNUM > 3 같은 조건은 첫 번째 행(ROWNUM=1)에서 실패하고, 다음 행에도 다시 ROWNUM=1이 부여되는 루프에 빠져 결과를 반환하지 않습니다. 반면 ROWNUM < 4는 첫 번째 행이 통과되어 ROWNUM=2가 부여되는 식으로 정상 동작합니다.DENSE_RANK()는 동점자에게 같은 순위를 부여하고, 다음 순위를 건너뛰지 않습니다(예: 1, 2, 2, 3). RANK()는 순위를 건너뜁니다(예: 1, 2, 2, 4). ROW_NUMBER()는 동일 순위를 허용하지 않고 연속된 번호를 부여합니다(예: 1, 2, 3, 4).