SELECT * FROM (
SELECT A.*, ROWNUM RN FROM (
SELECT * FROM table_name -- 內層:原始查詢 (可含 JOIN、WHERE、ORDER BY)
) A
WHERE ROWNUM <= 40 -- 中層:截取前 N 行
) WHERE RN >= 21; -- 外層:過濾起始行
查詢第 21-40 行 (每頁 20 行,第 2 頁)。
| 層級 | 角色 | 關鍵機制 |
|---|---|---|
| 內層 (Inner) | 原始查詢 | 執行完整查詢邏輯 (JOIN、過濾、排序) |
| 中層 (Middle) | ROWNUM 截斷 | COUNT STOPKEY:結果達到 ROWNUM 上限立即停止,不再產生後續行 |
| 外層 (Outer) | 篩選起始行 | 在中層結果集上過濾 RN >= start_row |
ROWNUM <= :max 推入內層查詢執行計畫-- 低效:中層無 ROWNUM 截斷
SELECT * FROM (
SELECT A.*, ROWNUM RN FROM (
SELECT * FROM table_name
) A
) WHERE RN BETWEEN 21 AND 40;
缺陷:BETWEEN 在外層,CBO 無法下推至內層 (內層不知 RN 含義)。內層必須產生所有符合條件行,中層賦予 ROWNUM,外層再過濾。大表效能差數數量級。
SELECT /*+ FIRST_ROWS(20) */ * FROM (
SELECT A.*, ROWNUM RN FROM (
SELECT * FROM table_name
) A
WHERE ROWNUM <= 40
) WHERE RN >= 21;
-- 內層先排序,中層截斷,外層過濾
SELECT * FROM (
SELECT A.*, ROWNUM RN FROM (
SELECT * FROM table_name ORDER BY created_date DESC
) A
WHERE ROWNUM <= 40
) WHERE RN >= 21;
關鍵:ORDER BY 必須在內層,確保截斷前已按業務邏輯排序。
CBO 面對分頁 + JOIN 可能選擇:
強制 NESTED LOOPS:
SELECT /*+ FIRST_ROWS(20) LEADING(t1) USE_NL(t2) */ * FROM (
SELECT A.*, ROWNUM RN FROM (
SELECT t1.*, t2.col FROM t1, t2 WHERE t1.id = t2.id ORDER BY t1.created DESC
) A
WHERE ROWNUM <= 40
) WHERE RN >= 21;
SELECT * FROM table_name
ORDER BY created_date DESC
OFFSET 20 ROWS FETCH NEXT 20 ROWS ONLY;
內部自動轉換為等效 ROWNUM 模式,語法更清晰,效能相同。
/*+ FIRST_ROWS(n) */ Hint (n = 每頁行數)EXPLAIN PLAN 確認出現 COUNT STOPKEY、NESTED LOOPSROWNUM <= 40 在外層也能工作 → 無法下推,全量掃描ROW_NUMBER() OVER() 分頁 → 需全量排序物化,大表極慢Oracle 分頁查詢的精髓在於:三層嵌套 + 中層 STOPKEY + 內層 ORDER BY + FIRST_ROWS Hint。這不只是語法模板,更是對 CBO 謂詞下推與提前終止機制的精準駕馭。掌握此模式,從萬行到億行,首頁查詢皆在毫秒級。