Oracle 無 SELECT TOP N 語法,標準做法:內層排序 + ROWNUM 過濾 + 外層排序。關鍵點:ROWNUM 在 ORDER BY 之前賦值,必須用子查詢先排序再取 ROWNUM。
SELECT * FROM (
SELECT * FROM customer ORDER BY name
)
WHERE ROWNUM <= 3
ORDER BY ROWNUM ASC;
結果:按 NAME 字母序前 3 筆(eighth, fifth, first)
SELECT id, name FROM (
SELECT ROWNUM recno, id, name FROM (
SELECT * FROM customer ORDER BY name
)
WHERE ROWNUM <= 3
ORDER BY ROWNUM ASC
)
WHERE recno = 2;
結果:第 2 筆(fifth)
SELECT id, name FROM (
SELECT ROWNUM recno, id, name FROM (
SELECT * FROM customer ORDER BY name
)
WHERE ROWNUM <= 2 -- 只取需要的前 N 筆
ORDER BY ROWNUM ASC
)
WHERE recno = 2;
減少中間結果集大小,效能更佳。
SELECT * FROM (
SELECT ROWNUM recno, id, name FROM (
SELECT * FROM customer ORDER BY name
)
WHERE ROWNUM <= (M + X - 1) -- 只取到需要的最大行
ORDER BY ROWNUM ASC
)
WHERE recno BETWEEN M AND (M + X - 1);
範例:第 2 筆起取 3 筆(M=2, X=3)→ ROWNUM <= 4,recno BETWEEN 2 AND 4
結果:fifth, first, forth
CREATE OR REPLACE PROCEDURE page_query(
p_sql IN VARCHAR2, -- 基礎查詢(不含 ORDER BY)
p_order_by IN VARCHAR2, -- 排序欄位
p_page_no IN NUMBER, -- 頁碼(從1開始)
p_page_size IN NUMBER, -- 每頁筆數
p_result OUT SYS_REFCURSOR
) AS
v_start NUMBER := (p_page_no - 1) * p_page_size + 1;
v_end NUMBER := p_page_no * p_page_size;
v_sql VARCHAR2(4000);
BEGIN
v_sql := 'SELECT * FROM (
SELECT ROWNUM rn, t.* FROM (
' || p_sql || ' ORDER BY ' || p_order_by || '
) t
WHERE ROWNUM <= ' || v_end || '
) WHERE rn >= ' || v_start;
OPEN p_result FOR v_sql;
END;
/
| 關鍵點 | 說明 |
|---|---|
| 子查詢先排序 | 確保 ROWNUM 對應業務排序邏輯 |
| 最小化中間結果集 | WHERE ROWNUM <= (M+X-1) 而非全表 |
| 索引支撐排序 | ORDER BY 欄位有索引可避免 SORT 操作 |
| 避免 SELECT * | 只選需要欄位,減少資料傳輸 |
-- 錯誤:ROWNUM 在 ORDER BY 前賦值,結果非預期
SELECT * FROM customer WHERE ROWNUM <= 3 ORDER BY name;
-- 正確:子查詢先排序
SELECT * FROM (SELECT * FROM customer ORDER BY name) WHERE ROWNUM <= 3;