SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT * FROM users u ORDER BY u.create_date DESC
) a WHERE ROWNUM <= 20
) b WHERE b.rn >= 0;
翻頁時出現重複行或數據「跳動」。
create_date 存在大量相同值 (如批量導入同一秒)。在 ORDER BY 後追加唯一列 (主鍵/唯一索引列):
SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT * FROM users u ORDER BY u.create_date DESC, u.user_id DESC
) a WHERE ROWNUM <= :page_size * :page_no
) b WHERE b.rn > :page_size * (:page_no - 1);
user_id 為主鍵,保證排序結果確定性。
SELECT * FROM users
ORDER BY create_date DESC, user_id DESC
OFFSET :offset ROWS FETCH NEXT :page_size ROWS ONLY;
OFFSET/FETCH 為 ANSI SQL 標準,12c+ 原生支持。WINDOW SORT PUSHED RANK 操作,避免子查詢嵌套。超大數據集 (百萬行+) 深分頁時,OFFSET 性能劣化嚴重。改用「記住上一頁最後一行的排序鍵值」:
-- 首頁
SELECT * FROM users
ORDER BY create_date DESC, user_id DESC
FETCH FIRST 20 ROWS ONLY;
-- 後續頁 (假設上一頁最後一行 create_date='2024-01-15 10:00:00', user_id=1005)
SELECT * FROM users
WHERE (create_date, user_id) < (TIMESTAMP '2024-01-15 10:00:00', 1005)
ORDER BY create_date DESC, user_id DESC
FETCH FIRST 20 ROWS ONLY;
優勢:無 OFFSET 掃描開銷,索引範圍掃描極快,適用無限滾動加載。
create_date, user_id) 需建立複合索引 支持排序與篩選。page_no, page_size) 做輸入校驗,防止注入或超大 OFFSET。