ROWNUM 是 Oracle 最獨特的偽欄位,**在行被篩選/輸出時才產生**,而非儲存於資料塊。這特性導致許多直觀寫法失效,本文詳細解析運作機制與正確用法。
-- 錯誤:試圖直接取第 30~49 行
SELECT * FROM all_objects WHERE ROWNUM BETWEEN 30 AND 49;
-- 結果:永遠回傳 0 列
**原因**:第一行讀取時 ROWNUM=1,不符合 BETWEEN 30 AND 49 被丟棄;第二行讀取時 ROWNUM 仍為 1 (因前一行未計號),同樣被丟棄 → 所有行皆被丟棄。
-- 步驟 1:先取前 49 行 (獲得 ROWNUM 1~49)
SELECT * FROM all_objects WHERE ROWNUM <= 49;
-- 步驟 2:包裝為子查詢,給 ROWNUM 別名,再過濾下限
SELECT * FROM (
SELECT ROWNUM rn, a.* FROM all_objects a WHERE ROWNUM <= 49
) WHERE rn >= 30;
SELECT * FROM (
SELECT ROWNUM rn, a.* FROM all_objects a WHERE ROWNUM <= :max_row
) WHERE rn >= :min_row;
使用 :max_row, :min_row 綁定變數,相同 SQL 文本可共用游標,減少 Library Cache 爭用。
→ 先分配 ROWNUM **再排序**,故 WHERE ROWNUM <= 10 ORDER BY created_date 會先取任意 10 列再排序,**非最新 10 筆**。
SELECT * FROM (
SELECT a.*, ROWNUM rn
FROM (SELECT * FROM all_objects ORDER BY created_date DESC) a
WHERE ROWNUM <= :max_row
) WHERE rn >= :min_row;
最內層先排序,中間層分配 ROWNUM 並截斷,外層過濾下限。
舊版 RBO 會利用 ROWNUM 進行「短路」:一旦滿足 ROWNUM 限制即停止查詢並回傳,不再處理剩餘資料。CBO 也有類似 **STOPKEY** 最佳化 (見 分頁查詢篇)。
「ROWNUM 隨行生、先篩後號、無法直接取中間、子查詢加別名、排序要最內、綁定變數共游標」