ROWNUM 是 Oracle 在**查詢結果集產生過程中**動態分配的序號,**永遠從 1 開始**,且**先有結果集、才有 ROWNUM**。這一點決定了它所有「反直覺」的行為。
ROWNUM > N 永無結果?SELECT * FROM t1 WHERE ROWNUM > 10;
> 10 → 捨棄結論:任何排除 ROWNUM=1 的條件(>, >=, =, BETWEEN ... AND 且下限>1)皆會導致空結果集。
| 錯誤寫法 | 結果 | 正確寫法(子查詢先固化 ROWNUM) |
|---|---|---|
WHERE ROWNUM > 10 | 0 行 | SELECT * FROM (SELECT ROWNUM rn, t.* FROM t) WHERE rn > 10 |
WHERE ROWNUM BETWEEN 5 AND 10 | 0 行 | SELECT * FROM (SELECT ROWNUM rn, t.* FROM t) WHERE rn BETWEEN 5 AND 10 |
WHERE ROWNUM != 10 | 前 9 行 | 同左(因 ROWNUM=10 被跳過後,後續行 ROWNUM 永遠卡在 10) |
SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT * FROM demo_table WHERE status = 'ACTIVE' ORDER BY create_time DESC
) a WHERE ROWNUM <= :endRow
) WHERE rn >= :startRow;
ROWNUM <= :endRow 利用「提前終止」特性,避免全表掃描。ROWNUM 為 rn,外層才能用 >= 篩選。ORDER BY 必須在最內層,否則排序在 ROWNUM 分配之後,分頁順序錯亂。| 特性 | ROWNUM | ROWID |
|---|---|---|
| 本質 | 邏輯序號(查詢產生時動態分配) | 物理地址(資料檔案+區塊+行號,唯一不變) |
| 穩定性 | 查詢條件改變即重新編號 | 行不移動(無 ROW MOVEMENT)則永不變 |
| 可作條件 | 僅支援 <, <=, !=(含 1) | 支援所有比較運算子,可精準定位單行 |
| 前綴限制 | 不可加表別名(t.ROWNUM 報錯) | 可加別名(t.ROWID) |
利用 ROWNUM <= N 的「提前終止」特性,Oracle 優化器會將其轉為 COUNT STOPKEY 操作,僅掃描前 N 行即停止:
-- 取最新 10 筆訂單(需配合索引)
SELECT * FROM (
SELECT * FROM orders ORDER BY order_date DESC
) WHERE ROWNUM <= 10;
此寫法在大表上比完整排序再取前 N 行快數量級。
SELECT * FROM demo_table
ORDER BY create_time DESC
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
WINDOW NOSORT STOPKEY,效能等同 ROWNUM 寫法。>, >=, =, BETWEEN(下限>1),必須用子查詢先固化。OFFSET ... FETCH,別再手寫 ROWNUM 了。