首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle ROWNUM 偽欄位完整指南:原理、陷阱與正確分頁寫法

     編輯:Oracle 資料庫基礎

核心概念:ROWNUM 是「結果集序號」,而非「表行號」

ROWNUM 是 Oracle 在**查詢結果集產生過程中**動態分配的序號,**永遠從 1 開始**,且**先有結果集、才有 ROWNUM**。這一點決定了它所有「反直覺」的行為。

為何 ROWNUM > N 永無結果?

SELECT * FROM t1 WHERE ROWNUM > 10;
  1. 讀取第一行 → ROWNUM=1 → 不滿足 > 10 → 捨棄
  2. 讀取第二行 → ROWNUM 仍為 1(因第一行未計入結果集) → 捨棄
  3. 循環往複,永無行能讓 ROWNUM 變成 11

結論:任何排除 ROWNUM=1 的條件(>, >=, =, BETWEEN ... AND 且下限>1)皆會導致空結果集。

常見誤區與正確寫法對照

錯誤寫法結果正確寫法(子查詢先固化 ROWNUM)
WHERE ROWNUM > 100 行SELECT * FROM (SELECT ROWNUM rn, t.* FROM t) WHERE rn > 10
WHERE ROWNUM BETWEEN 5 AND 100 行SELECT * FROM (SELECT ROWNUM rn, t.* FROM t) WHERE rn BETWEEN 5 AND 10
WHERE ROWNUM != 10前 9 行同左(因 ROWNUM=10 被跳過後,後續行 ROWNUM 永遠卡在 10)

標準分頁範本(Oracle 11g 及之前)

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 vs ROWID:偽欄位雙雄的本質區別

特性ROWNUMROWID
本質邏輯序號(查詢產生時動態分配)物理地址(資料檔案+區塊+行號,唯一不變)
穩定性查詢條件改變即重新編號行不移動(無 ROW MOVEMENT)則永不變
可作條件僅支援 <, <=, !=(含 1)支援所有比較運算子,可精準定位單行
前綴限制不可加表別名(t.ROWNUM 報錯)可加別名(t.ROWID)

進階技巧:TOP-N 查詢優化

利用 ROWNUM <= N 的「提前終止」特性,Oracle 優化器會將其轉為 COUNT STOPKEY 操作,僅掃描前 N 行即停止:

-- 取最新 10 筆訂單(需配合索引)
SELECT * FROM (
  SELECT * FROM orders ORDER BY order_date DESC
) WHERE ROWNUM <= 10;

此寫法在大表上比完整排序再取前 N 行快數量級。

Oracle 12c+ 的現代替代方案

SELECT * FROM demo_table
ORDER BY create_time DESC
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
  • 語法標準、可讀性強、支援任意起始位置。
  • 執行計畫同樣會出現 WINDOW NOSORT STOPKEY,效能等同 ROWNUM 寫法。
  • 建議:新專案優先使用標準語法,舊系統維護時才用 ROWNUM 寫法。

總結

  1. ROWNUM 是**結果集流水號**,永遠從 1 開始,先有結果集才有 ROWNUM。
  2. 絕不直接用 >, >=, =, BETWEEN(下限>1),必須用子查詢先固化。
  3. 分頁標準三層嵌套:內層排序+截斷,中層固化 ROWNUM,外層篩選範圍。
  4. ROWID 才是物理定位利器,ROWNUM 只適合邏輯分頁與 TOP-N。
  5. 12c+ 環境請直接用 OFFSET ... FETCH,別再手寫 ROWNUM 了。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved