현재 선택한 SQL 과정

SQLP 이론 학습

이론 목록으로 돌아가기

Top-N 처리 원리: ROWNUM·FETCH FIRST·STOPKEY

정렬 기준의 앞쪽 N건을 구할 때 ROWNUM 위치와 FETCH FIRST 문법, COUNT·SORT ORDER BY STOPKEY의 실제 읽기 범위를 이해합니다.

예상 읽기 22

핵심 요약

Top-N 조회는 단순히 N건을 반환하는 조회가 아닙니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
Top-N
= 전체 후보에 업무 정렬 기준을 적용
+ 정렬 기준의 앞쪽 N건을 선택

성능 판단의 핵심입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
최종 결과 Row = N건

하지만 실제 작업량은
- 후보를 몇 행 읽었는가?
- 정렬 Key를 몇 행 계산했는가?
- Sort Workarea·TEMP를 얼마나 사용했는가?
- Index·Table Block을 얼마나 읽었는가?
- N건을 찾은 뒤 하위 처리를 실제로 멈췄는가?
로 결정

대표 실행 형태입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
입력이 이미 필요한 순서
→ 앞에서 N건 확보
→ 하위 Row Source 요청 중단 가능
→ COUNT STOPKEY·WINDOW NOSORT STOPKEY 등

입력이 필요한 순서가 아님
→ 후보를 읽으면서 상위 N개 유지
→ SORT ORDER BY STOPKEY
→ 입력 전체를 읽을 수도 있음

전체 정렬 후 외부에서 제한
→ 불필요한 Full Sort가 발생할 수 있음

이 이론의 범위

이 이론은 SQLP의 SQL 고급활용 및 튜닝 → 소트 튜닝 → Top-N·STOPKEY 범위에서 ROWNUM, Row Limiting Clause, 결정적 ORDER BY, Index Order와 실행계획 검증을 다룹니다.


학습 목표

이 이론을 학습한 뒤에는 다음을 설명할 수 있어야 합니다.

  • Top-N 조회와 임의 N건 제한을 구분한다.
  • ROWNUM이 Row가 선택되는 과정에서 부여되는 Pseudocolumn임을 설명한다.
  • 같은 Query Block의 ROWNUM과 ORDER BY 순서가 결과를 바꾸는 이유를 설명한다.
  • ROWNUM Inline View Top-N 구조를 작성한다.
  • OFFSET, FETCH FIRST, PERCENT, ONLY, WITH TIES의 의미를 설명한다.
  • 결정적 ORDER BY와 Tie-Breaker의 필요성을 설명한다.
  • COUNT STOPKEYSORT ORDER BY STOPKEY의 처리 차이를 설명한다.
  • STOPKEY 출력 Row와 하위 Access A-Rows를 구분한다.
  • Index Range·Full Scan과 Index Fast Full Scan의 순서 제공 차이를 설명한다.
  • 추가 Table Filter가 Index 조기 종료를 지연시킬 수 있음을 설명한다.
  • Join 전후 Top-N의 대상 Grain을 구분한다.
  • ALLSTATS LAST에서 A-Rows·Buffers·Starts로 실제 효율을 검증한다.

1. Top-N과 단순 Row 제한

요구사항입니다.

전체 주문 중 금액이 가장 큰 주문 10건을 반환한다.

두 단계가 필요합니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
1. 전체 후보에서 주문금액 기준 순서를 결정
2. 그 순서의 앞쪽 10건 선택

다음 SQL은 전체 상위 10건을 보장하지 않습니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT order_id,
       order_amount
FROM   orders
WHERE  ROWNUM <= 10
ORDER BY order_amount DESC;

개념적 의미입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
Access Path에서 먼저 발견된 10건 선택
→ 선택된 10건만 금액순 정렬

따라서 구분합니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
임의 10건을 정렬
≠ 전체 후보의 상위 10건

2. ROWNUM의 적용 시점

ROWNUM은 Oracle이 Table 또는 Join Row Source에서 Row를 선택해 반환하는 순서를 나타내는 Pseudocolumn입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
첫 번째 선택 Row → ROWNUM 1
두 번째 선택 Row → ROWNUM 2
...

같은 Query Block에서 다음이 함께 있으면:

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
WHERE ROWNUM <= 10
ORDER BY ...

ROWNUM으로 Row를 제한한 뒤 ORDER BY가 선택된 Row를 재정렬할 수 있습니다. Access Path가 바뀌면 선택되는 Row도 달라질 수 있습니다.

2.1 올바른 ROWNUM Top-N

순서를 안쪽 Query Block에서 먼저 정의합니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT order_id,
       order_amount
FROM (
    SELECT order_id,
           order_amount
    FROM   orders
    ORDER BY order_amount DESC,
             order_id DESC
)
WHERE ROWNUM <= 10;

처리 의미입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
안쪽 Query Block
→ 전체 후보의 결정적 순서 정의

바깥 Query Block
→ 정렬 결과의 앞쪽 10건 제한

2.2 ROWNUM > 1 조건

다음 조건은 Row를 반환하지 않습니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
WHERE ROWNUM > 1

첫 후보 Row가 ROWNUM 1을 받아 조건을 실패하면, 다음 후보도 다시 첫 반환 후보가 되어 ROWNUM 1을 받으므로 계속 실패합니다.

Pagination은 ROWNUM 범위를 같은 단계에서 단순하게 작성하지 않고, 중첩 Query Block이나 OFFSET ... FETCH를 사용합니다.

2.3 ROWNUM과 View Optimization

ROWNUM을 Query에 사용하면 View Optimization·Transformation에 영향을 줄 수 있습니다. SQL Text의 Inline View 경계와 실제 실행계획의 Query Block·Operation을 함께 확인합니다.


3. Row Limiting Clause

Oracle은 SELECT의 row_limiting_clause로 반환 Row를 제한할 수 있습니다.

3.1 FETCH FIRST

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT order_id,
       order_amount
FROM   orders
ORDER BY order_amount DESC,
         order_id DESC
FETCH FIRST 10 ROWS ONLY;

의미입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
ORDER BY로 순서를 정의
→ 앞쪽 10행 반환

FIRSTNEXT, ROWROWS는 문법적으로 의미 차이가 없습니다.

3.2 OFFSET

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
ORDER BY order_amount DESC,
         order_id DESC
OFFSET 20 ROWS
FETCH NEXT 10 ROWS ONLY;

앞쪽 20행을 건너뛰고 다음 10행을 반환합니다.

주의합니다.

  • 큰 OFFSET은 앞쪽 Row를 처리하고 버리는 비용이 커질 수 있음
  • Pagination 중 Data가 변경되면 Page 간 누락·중복이 생길 수 있음
  • 안정적 Pagination에는 결정적 ORDER BY가 필요
  • 대규모 Pagination은 마지막 Key를 조건에 사용하는 Keyset Pagination도 비교

3.3 PERCENT

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
FETCH FIRST 10 PERCENT ROWS ONLY;

선택된 전체 결과의 앞쪽 10%를 반환합니다. 정확한 Row 수가 필요한 업무에는 고정 Row Count와 의미가 다릅니다.

3.4 ONLY

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
FETCH FIRST 10 ROWS ONLY

사용 가능한 Row가 충분하다면 정확히 10행을 반환합니다.

3.5 WITH TIES

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
ORDER BY score DESC
FETCH FIRST 3 ROWS WITH TIES;

세 번째 Row와 모든 ORDER BY 표현식의 값이 같은 추가 Row를 함께 반환합니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
정렬 점수: 100, 95, 90, 90, 90, 80

FETCH FIRST 3 ROWS WITH TIES
→ 100, 95, 90, 90, 90
→ 5행 반환

WITH TIES는 ORDER BY가 필요합니다.


4. 결정적 ORDER BY

다음 SQL은 점수가 같은 Row 사이의 순서를 완전히 정의하지 않습니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT student_id,
       score
FROM   exam_result
ORDER BY score DESC
FETCH FIRST 10 ROWS ONLY;

경계에 동일 점수 Row가 여러 건 있으면 어떤 Row가 10건에 포함되는지 비결정적일 수 있습니다.

결정적 정렬입니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
ORDER BY score DESC,
         student_id ASC
FETCH FIRST 10 ROWS ONLY;

student_id가 Unique하면 모든 Row의 전체 순서가 결정됩니다.

4.1 ONLY와 Tie-Breaker

Tie-Breaker는 ONLY의 결과를 안정화합니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
동점 중 어떤 Row가 포함되는가?
→ Unique Tie-Breaker가 결정

4.2 WITH TIES와 Tie-Breaker

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
ORDER BY score DESC
FETCH FIRST 3 ROWS WITH TIES;

점수가 같은 모든 경계 Row를 포함합니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
ORDER BY score DESC,
         student_id ASC
FETCH FIRST 3 ROWS WITH TIES;

Unique student_id까지 모든 ORDER BY 값이 같은 서로 다른 Row는 일반적으로 없으므로 결과는 정확히 3행 방향이 됩니다.

즉, WITH TIES의 Tie 기준은 첫 Sort Key 하나가 아니라 ORDER BY 표현식 전체입니다.


5. COUNT STOPKEY

입력이 이미 원하는 순서로 들어오면 필요한 Row를 얻은 후 하위 Row Source 요청을 멈출 수 있습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
COUNT STOPKEY
  TABLE ACCESS BY INDEX ROWID ORDERS
    INDEX RANGE SCAN DESCENDING ORDERS_AMT_IX

개념적 흐름입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
Index에서 가장 큰 Key부터 순서대로 읽음
→ Table Row 조회
→ 조건을 만족한 결과 N건 확보
→ 하위 Row 요청 중단

장점입니다.

  • 일반 전체 Sort 제거 가능
  • 적은 Index Leaf·Table Block만 읽고 중단 가능
  • First Row·첫 Page 응답 개선 가능

하지만 Operation 이름만으로 N개의 Index Entry만 읽었다고 단정하지 않습니다.

다음이 있으면 더 많은 후보를 확인할 수 있습니다.

  • Index에 없는 Table Filter
  • Join 후 Filter
  • Key당 중복 Row
  • 조건에 맞지 않는 후보 Row
  • OFFSET
  • WITH TIES의 경계 동점
  • Table by ROWID 비용

6. SORT ORDER BY STOPKEY

입력이 필요한 순서가 아니면 Oracle은 후보를 읽으며 상위 N개를 유지하는 Top-N Sort를 사용할 수 있습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
SORT ORDER BY STOPKEY
  TABLE ACCESS FULL ORDERS

6.1 일반 Full Sort와 차이

일반 Full Sort:

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
모든 후보를 Sort Workarea·TEMP Run에 정렬
→ 전체 정렬 결과 생성

Top-N Sort:

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
후보를 읽으면서 현재 상위 N개 중심으로 유지
→ 전체 Row를 모두 Memory에 보관하지 않을 수 있음

따라서 Workarea Memory를 줄일 가능성이 있습니다.

6.2 입력 전체를 읽을 수 있는 이유

정렬되지 않은 후보에서 상위 N개를 확정하려면 마지막 Row까지 더 큰 값이 있는지 확인해야 할 수 있습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
SORT ORDER BY STOPKEY A-Rows = 10
TABLE ACCESS FULL A-Rows     = 50,000,000

해석입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
출력은 10행
하지만 상위값 확정을 위해
하위 후보 5천만 행을 읽음

Top-N 최적화 여부는 출력 Row가 아니라 하위 Access의 A-Rows·Buffers·Reads로 판단합니다.


7. Index Order를 이용한 Top-N

조회입니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT order_id,
       customer_id,
       order_date
FROM   orders
WHERE  customer_id = :customer_id
ORDER BY order_date DESC,
         order_id DESC
FETCH FIRST 20 ROWS ONLY;

후보 Index입니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
CREATE INDEX orders_cust_date_ix
ON orders(
    customer_id,
    order_date DESC,
    order_id DESC
);

정렬 활용 조건입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
customer_id
→ Equality로 한 고객의 Index 범위 고정

order_date DESC, order_id DESC
→ 해당 범위 안에서 요청한 순서 제공

FETCH FIRST 20
→ 앞쪽 Match 20건 확보 후 중단 가능

7.1 순서를 제공할 수 있는 Scan

Plan 조건에 따라 다음 Scan은 Index Key 순서로 RowID를 제공할 수 있습니다.

  • INDEX RANGE SCAN
  • INDEX RANGE SCAN DESCENDING
  • INDEX FULL SCAN
  • INDEX FULL SCAN DESCENDING

7.2 INDEX FAST FULL SCAN

INDEX FAST FULL SCAN은 Multiblock I/O로 Index Block을 Disk에 존재하는 순서대로 읽습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
INDEX FULL SCAN
→ Index Key 순서
→ Sort 생략 가능성

INDEX FAST FULL SCAN
→ 정렬되지 않은 Block 순서
→ Sort 생략용 순서 제공 불가

7.3 선두 Column과 전체 순서

Index가 다음과 같아도:

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
(customer_id, order_date DESC, order_id DESC)

여러 customer_id를 동시에 조회하면 각 고객 범위 안에서만 날짜순일 뿐 전체 결과가 order_date DESC로 합쳐지는 것은 아닙니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
customer 10의 날짜순 구간
→ customer 20의 날짜순 구간

전체 날짜순
≠ 보장

8. Table Filter와 조기 종료

Index가 정렬 순서를 제공해도 Table Filter 때문에 N개보다 많은 후보를 확인할 수 있습니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT order_id,
       order_date,
       amount
FROM   orders
WHERE  customer_id = :customer_id
AND    status = 'COMPLETED'
ORDER BY order_date DESC,
         order_id DESC
FETCH FIRST 20 ROWS ONLY;

Index:

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
(customer_id, order_date DESC, order_id DESC)

status가 Index에 없다면:

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
Index Entry 읽음
→ ROWID Table Access
→ status Filter
→ 탈락 가능

최근 주문 100건 중 완료 주문이 20건이면 20건을 반환하려고 약 100개 후보와 Table Row를 읽을 수 있습니다.

대안 Index:

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
(customer_id, status, order_date DESC, order_id DESC)

검토 기준입니다.

  • status의 선택도·Skew
  • 다른 Query의 Predicate
  • Index 폭·DML 비용
  • Covering 가능성
  • 실제 후보 A-Rows·Table Buffers

9. Covering Index와 Top-N

다음 조회가 필요한 Column을 모두 Index에서 얻을 수 있다면 Table by ROWID를 생략할 수 있습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
(customer_id, order_date DESC, order_id DESC)

조회 Column:

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
customer_id, order_date, order_id

가능한 Plan입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
COUNT STOPKEY
  INDEX RANGE SCAN DESCENDING ORDERS_CUST_DATE_IX

장점입니다.

  • 정렬 생략
  • Table Access 제거
  • N건 확보 후 조기 종료 가능

Trade-off입니다.

  • Index Column·Entry 폭 증가
  • LEAF_BLOCKS 증가
  • Buffer Cache 점유
  • INSERT·UPDATE·DELETE 유지 비용
  • Redo·Undo·공간 증가

한 Top-N SQL만 보고 무조건 Wide Covering Index를 만들지 않습니다.


10. Top-N 대상 Grain

Top-N을 Join 전으로 이동하면 업무 대상이 달라질 수 있습니다.

10.1 Join Row Top-N

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT o.order_id,
       i.item_id,
       o.order_date
FROM   orders o
JOIN   order_items i
  ON   i.order_id = o.order_id
ORDER BY o.order_date DESC,
         o.order_id DESC,
         i.item_id
FETCH FIRST 20 ROWS ONLY;

의미입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
주문·상품 조합 Row 중 상위 20행

10.2 주문 Top-N 후 Detail Join

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
WITH recent_order AS (
    SELECT order_id,
           order_date
    FROM   orders
    ORDER BY order_date DESC,
             order_id DESC
    FETCH FIRST 20 ROWS ONLY
)
SELECT r.order_id,
       i.item_id,
       r.order_date
FROM   recent_order r
JOIN   order_items i
  ON   i.order_id = r.order_id;

의미입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
최근 주문 20건 선택
→ 각 주문의 모든 상품 반환
→ 최종 결과 20행 초과 가능

두 SQL은 1:N 관계에서 동등하지 않습니다.

10.3 Join Filter와 Top-N

“VIP 고객 주문 상위 20건”이라면 VIP Filter를 적용한 집합에서 순위를 정해야 합니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
모든 주문 Top 20 선선택
→ VIP Join·Filter

≠

VIP 주문 집합
→ Top 20

Join·Filter가 Top-N 포함 집합을 바꾸는지 확인합니다.


11. OFFSET Pagination과 Keyset Pagination

OFFSET 방식입니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
ORDER BY order_date DESC,
         order_id DESC
OFFSET 100000 ROWS
FETCH NEXT 20 ROWS ONLY;

큰 Page로 갈수록 앞쪽 100,000행을 찾아 건너뛰는 비용이 커질 수 있습니다.

Keyset Pagination 예시입니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
WHERE (
       order_date < :last_order_date
    OR (
       order_date = :last_order_date
       AND order_id < :last_order_id
    )
)
ORDER BY order_date DESC,
         order_id DESC
FETCH FIRST 20 ROWS ONLY;

장점입니다.

  • 마지막으로 본 결정적 Key 뒤에서 탐색
  • 적절한 Index에서 큰 OFFSET 처리 감소 가능
  • Data 변경 중 상대적으로 Page 중복·누락을 줄일 수 있음

주의합니다.

  • 정렬 Key 방향·NULL 의미를 정확히 구현
  • Unique Tie-Breaker 필요
  • 임의 Page 번호 직접 이동에는 별도 설계 필요

12. Top-N per Group

부서별 상위 급여 3명처럼 Group별 Top-N은 전체 FETCH FIRST와 다른 요구입니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT employee_id,
       department_id,
       salary
FROM (
    SELECT employee_id,
           department_id,
           salary,
           ROW_NUMBER() OVER (
               PARTITION BY department_id
               ORDER BY salary DESC,
                        employee_id
           ) AS rn
    FROM employees
)
WHERE rn <= 3;

핵심입니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
전체 결과 Top 3
≠ 부서별 Top 3

ROW_NUMBER도 ORDER BY가 Total Order를 만들지 않으면 Tie Row의 번호가 비결정적일 수 있으므로 Unique Tie-Breaker를 사용합니다.

Oracle 26ai의 Partitioned Row Limiting Clause를 사용할 수 있는 환경도 있지만, 기존 Version 호환성과 업무 요구를 고려해 Analytic Function 방식과 비교합니다.


13. 실행계획 검증

실제 수행 통계를 수집합니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT /*+ GATHER_PLAN_STATISTICS */
       order_id,
       customer_id,
       order_date
FROM   orders
WHERE  customer_id = :customer_id
ORDER BY order_date DESC,
         order_id DESC
FETCH FIRST 20 ROWS ONLY;
SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT *
FROM TABLE(
    DBMS_XPLAN.DISPLAY_CURSOR(
        :sql_id,
        :child_no,
        'ALLSTATS LAST +MEMSTATS +PREDICATE +ALIAS +NOTE'
    )
);

13.1 확인 항목

지표질문
OperationCOUNT STOPKEY·SORT ORDER BY STOPKEY·WINDOW NOSORT STOPKEY 중 무엇인가?
하위 A-RowsN건을 얻기 위해 후보를 몇 행 읽었는가?
Buffers·ReadsIndex·Table·Full Scan에서 Block을 얼마나 읽었는가?
Starts하위 Operation이 반복됐는가?
PredicateAccess Predicate와 Table Filter 위치는 어디인가?
Used-Mem·Used-TmpTop-N Sort가 Memory·TEMP를 얼마나 사용했는가?
결과 A-RowsONLY·WITH TIES·OFFSET 요구와 일치하는가?
NoteTransformation·Adaptive Plan이 있었는가?

13.2 대표 비교

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
Plan A
COUNT STOPKEY
Index A-Rows 20
Table Buffers 25

Plan B
SORT ORDER BY STOPKEY
Full Scan A-Rows 50,000,000
Buffers 400,000
Sort Output 20

Plan A가 Top-N 조기 종료에 성공한 방향입니다.

하지만 다음 Plan도 가능합니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
COUNT STOPKEY
Index 후보 A-Rows 500,000
Table A-Rows 20
Buffers 600,000

Index가 순서를 제공했어도 Table Filter 탈락이 크면 비효율일 수 있습니다.


14. First Row와 End-of-Fetch

Top-N 업무는 일반적으로 전체 대량 결과보다 First Page 응답이 중요합니다.

비교 조건을 통일합니다.

  • 같은 Bind·Data Type
  • 같은 N·OFFSET·WITH TIES
  • 같은 결정적 ORDER BY
  • 같은 Client Fetch Size
  • 같은 Projection
  • 같은 결과 Row
  • 유사한 Cache 상태

측정합니다.

  • 첫 Row 시간
  • N번째 Row 시간
  • End-of-Fetch 시간
  • Buffers·Reads
  • CPU
  • TEMP
  • 동시 실행 영향

Cost 숫자만으로 첫 Page 성능을 확정하지 않습니다.


15. 실전 적용 순서

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
1. Top-N 대상 Entity·Join Grain을 정의한다.
2. ONLY 또는 WITH TIES를 결정한다.
3. 결정적 ORDER BY와 Unique Tie-Breaker를 정한다.
4. ROWNUM Query Block 또는 FETCH FIRST 위치를 확인한다.
5. Predicate 후 후보 Row 수와 추가 Table Filter를 확인한다.
6. Index가 전체 정렬 순서를 제공하는지 검토한다.
7. COUNT·SORT STOPKEY와 하위 A-Rows를 확인한다.
8. Covering·복합 Index와 DML Trade-off를 비교한다.
9. OFFSET이 크면 Keyset Pagination을 검토한다.
10. 동일 결과·Bind·Fetch에서 First Row와 전체 Runtime을 검증한다.

자주 혼동하는 판단

혼동정확한 기준
ROWNUM <= N이면 항상 상위 N정렬과 Row 제한의 Query Block 위치 확인
FETCH FIRST는 ORDER BY 없이도 안정적결정적 ORDER BY 필요
WITH TIES는 첫 Sort Key만 비교ORDER BY 표현식 전체의 Tie
출력 N건이면 입력도 N건하위 Access A-Rows·Buffers 확인
STOPKEY가 있으면 Sort 없음COUNT STOPKEY와 SORT ORDER BY STOPKEY 구분
SORT STOPKEY면 Table도 N건만 읽음정렬되지 않은 입력은 전체 Scan 가능
Index가 있으면 Sort 제거Prefix·방향·NULL·후속 Operation 확인
Fast Full Scan이 정렬 순서 제공Fast Full Scan은 순서 보장 없음
Top-N을 Join 전에 옮기면 항상 빠르고 동일대상 Grain·Filter·중복 확인
Cost가 가장 낮으면 첫 Page도 가장 빠름동일 Fetch 계약의 Runtime으로 검증

스스로 확인하기

개념 확인 문제

문제를 누르면 바로 아래에서 정답과 해설을 확인할 수 있습니다.

01Top-N 조회와 임의 N건 제한의 차이를 설명하시오.
정답 및 해설

Top-N과 임의 제한

  • Top-N은 전체 후보에 업무 정렬 기준을 적용한 뒤 앞쪽 N건을 선택합니다.
  • 임의 N건 제한은 Access Path에서 먼저 발견된 N건일 수 있으며 전체 상위 N건을 보장하지 않습니다.
02같은 Query Block에서 ROWNUM 조건과 ORDER BY를 함께 사용할 때 결과가 달라질 수 있는 이유를 설명하시오.
정답 및 해설

ROWNUM과 ORDER BY

  • ROWNUM은 Row가 선택되는 과정에 부여됩니다.
  • 같은 Query Block에서 ROWNUM으로 먼저 N건을 제한하면 ORDER BY는 선택된 N건만 재정렬할 수 있습니다.
  • Access Path가 달라지면 선택되는 Row도 달라질 수 있습니다.
03ROWNUM Inline View Top-N의 기본 구조를 설명하시오.
정답 및 해설

Inline View 구조

  • 안쪽 Query Block에서 결정적 ORDER BY로 전체 순서를 정의합니다.
  • 바깥 Query Block에서 ROWNUM <= N을 적용합니다.
  • 예: SELECT * FROM (SELECT ... ORDER BY ...) WHERE ROWNUM <= 10.
04FETCH FIRST의 ONLY와 WITH TIES 차이를 설명하시오.
정답 및 해설

ONLY·WITH TIES

  • ONLY는 사용 가능한 Row가 충분하면 지정한 수만 반환합니다.
  • WITH TIES는 마지막 Row와 모든 ORDER BY 표현식 값이 같은 추가 Row를 함께 반환하므로 N보다 많을 수 있습니다.
05결정적 ORDER BY와 Unique Tie-Breaker가 필요한 이유를 설명하시오.
정답 및 해설

결정적 정렬

  • 경계에 동일 Sort Key Row가 있으면 어떤 Row가 포함되는지 비결정적일 수 있습니다.
  • Unique ID 같은 Tie-Breaker를 추가하면 전체 Row 순서가 고정됩니다.
  • ROW_NUMBER·Pagination에도 같은 원칙이 적용됩니다.
06COUNT STOPKEY와 SORT ORDER BY STOPKEY의 핵심 차이를 설명하시오.
정답 및 해설

두 STOPKEY

  • COUNT STOPKEY는 이미 필요한 순서로 들어오는 Row Source를 N건 확보 후 중단할 수 있습니다.
  • SORT ORDER BY STOPKEY는 정렬되지 않은 입력에서 상위 N개 후보를 유지하는 Sort이며 입력 전체를 읽을 수 있습니다.
07SORT ORDER BY STOPKEY 출력은 N건인데 하위 Full Scan이 전체 후보를 읽을 수 있는 이유를 설명하시오.
정답 및 해설

전체 후보 Scan

  • 정렬되지 않은 입력에서는 마지막 후보에 더 큰 값이 있을 수 있습니다.
  • 상위 N을 확정하려면 모든 후보의 Sort Key를 확인해야 할 수 있습니다.
  • 따라서 Sort Output N과 하위 Access A-Rows는 다릅니다.
08Index Order Top-N에서 추가 Table Filter가 조기 종료를 지연시키는 이유를 설명하시오.
정답 및 해설

Table Filter

  • Index가 순서대로 후보 RowID를 제공해도 Index에 없는 조건은 Table Row를 읽은 뒤 평가합니다.
  • 탈락 Row가 많으면 N건을 반환하려고 N개보다 많은 Index Entry와 Table Block을 읽습니다.
09Join 전 Top-N 이동이 결과를 바꾸는 대표 사례를 설명하시오.
정답 및 해설

Join 전 Top-N

  • 주문·상품 Join Row 상위 20행과 주문 20건을 먼저 선택한 뒤 모든 상품을 붙이는 결과는 다릅니다.
  • VIP 고객 Filter처럼 Join 조건이 Top-N 포함 집합을 바꾸는 경우에도 선선택하면 올바른 Row가 누락될 수 있습니다.
10Top-N Plan을 실행 통계로 검증하는 절차를 설명하시오.
정답 및 해설

실행 통계 검증 - COUNT·SORT STOPKEY Operation을 확인합니다. - 하위 Index·Table·Full Scan의 A-Rows, Buffers, Reads, Starts를 확인합니다. - Access Predicate와 Table Filter를 구분합니다. - Used-Mem·Used-Tmp와 ONLY·WITH TIES 결과 행 수를 확인합니다. - 동일 Bind·정렬·Fetch 조건에서 첫 Row와 End-of-Fetch를 비교합니다.