Oracle 的 ROWNUM 在 WHERE/ORDER BY 之前賦值,導致 WHERE ROWNUM BETWEEN 5000 AND 10000 永無結果 — 必須用巢狀子查詢或 ROW_NUMBER() 解決。
SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT * FROM vehicle ORDER BY make
) a WHERE ROWNUM <= 10000
) WHERE rn >= 5000;
SELECT * FROM (
SELECT v.*, ROW_NUMBER() OVER (ORDER BY make) AS rn
FROM vehicle v
) WHERE rn BETWEEN 5000 AND 10000;
WINDOW SORT PUSHED RANK 早期停止排序。OFFSET ... FETCH (ANSI SQL:2008):SELECT * FROM vehicle ORDER BY make
OFFSET 4999 ROWS FETCH NEXT 5000 ROWS ONLY;
當基礎表極大 (千萬列) 時,先在 WITH 中完成過濾、彙總、連接,再對結果集分頁:
WITH filtered AS (
SELECT /*+ PARALLEL(4) */ registration_no, make, model
FROM vehicle
WHERE last_service_date >= DATE '2023-01-01'
AND category = 'A'
)
SELECT * FROM (
SELECT f.*, ROW_NUMBER() OVER (ORDER BY make) rn
FROM filtered f
) WHERE rn BETWEEN 5000 AND 10000;
PARALLEL, NO_MERGE) 控制執行計畫。| 寫法 | 耗時 | Temp 使用 | 備註 |
|---|---|---|---|
| 三層 ROWNUM | 2.8s | 120 MB | 全表排序後截取 |
| ROW_NUMBER() | 1.9s | 95 MB | 早期停止排序 |
| OFFSET/FETCH (12c) | 1.8s | 90 MB | 語法最簡潔 |
| WITH + ROW_NUMBER() | 0.6s | 15 MB | 預先過濾 90% 資料 |
ORDER BY 欄位有索引,最佳化器可執行 INDEX FULL SCAN 避免排序。WHERE + ORDER BY 可實現 INDEX RANGE SCAN 直接取 Top-N。ROWNUM BETWEEN ...。ORDER BY。WHERE id > last_seen_id ORDER BY id FETCH FIRST 50)。DBMS_STATS.GATHER_TABLE_STATS。12c+ 首選 OFFSET/FETCH;10g/11g 用 ROW_NUMBER();超大表務必結合 WITH 預先縮小資料集;極大偏移量改用 Keyset 分頁。