Oracle 無 LIMIT/OFFSET,需利用 ROWNUM 偽欄位配合子查詢實現。關鍵在於 先限制上限再過濾下限,避免全表掃描。
SELECT *
FROM (
SELECT ROWNUM rn, t.*
FROM k_task t
WHERE flight_date BETWEEN DATE '2006-05-01' AND DATE '2006-07-31'
AND ROWNUM <= 20 -- 關鍵:內層先截取前 20 筆
) WHERE rn >= 10; -- 外層再過濾第 10-20 筆
優點:只掃描必要資料量,效能不隨資料量增長而劣化。
SELECT *
FROM (
SELECT tt.*, ROWNUM rn
FROM (
SELECT t.*
FROM k_task t
WHERE flight_date BETWEEN DATE '2006-05-01' AND DATE '2006-05-31'
ORDER BY fact_up_time, flight_no
) tt
WHERE ROWNUM <= 20
) WHERE rn >= 10;
注意:排序會導致全範圍掃描,資料量大時效能下降。
-- 無 ORDER BY
SELECT * FROM (
SELECT ROWNUM rn, t.* FROM k_task t
WHERE flight_date BETWEEN ...
) WHERE rn BETWEEN 10 AND 20;
-- 有 ORDER BY
SELECT * FROM (
SELECT tt.*, ROWNUM rn FROM (
SELECT t.* FROM k_task t
WHERE ... ORDER BY fact_up_time, flight_no
) tt
) WHERE rn BETWEEN 10 AND 20;
缺點:內層無 ROWNUM <= 上限 限制,需掃描全部符合條件資料。
-- 有 ORDER BY
WITH partdata AS (
SELECT ROWNUM rn, tt.*
FROM (
SELECT t.* FROM k_task t
WHERE flight_date BETWEEN ... ORDER BY fact_up_time, flight_no
) tt
WHERE ROWNUM <= 20
)
SELECT * FROM partdata WHERE rn >= 10;
-- 無 ORDER BY
WITH partdata AS (
SELECT ROWNUM rn, t.*
FROM k_task t
WHERE flight_date BETWEEN ... AND ROWNUM <= 20
)
SELECT * FROM partdata WHERE rn >= 10;
優點:語義清晰,易維護;效能與對應嵌套寫法相同。
| 場景 | 推薦寫法 | 關鍵點 |
|---|---|---|
| 無排序 | 方案 1 (單層 + ROWNUM <= 上限) | 最快,適合大數據量 |
| 需排序 | 方案 2 (雙層 + 內層排序 + ROWNUM <= 上限) | 排序成本不可避免,建議加索引 |
| 可讀性優先 | WITH 子句對應版本 | 邏輯分層清楚,團隊協作友善 |
避坑指南:永遠不要在最外層直接用
ROWNUM BETWEEN而省略內層上限限制,否則大資料量下會全表掃描導致效能災難。