分頁是 Web 應用核心需求。本文整理三種主流 T-SQL 分頁寫法、效能特性,並針對「最新資料在前」的倒序場景給出優化寫法。
SELECT TOP @PageSize *
FROM table1
WHERE id NOT IN (
SELECT TOP @PageSize * (@PageNum - 1) id
FROM table1 ORDER BY id
)
ORDER BY id;
NOT IN 比較,隨頁碼增大急劇變慢。NULL 值 (NOT IN 含 NULL 回傳空集)。-- 正序
SELECT TOP @PageSize *
FROM table1
WHERE id > (
SELECT ISNULL(MAX(id),0)
FROM (SELECT TOP @PageSize * (@PageNum - 1) id FROM table1 ORDER BY id) t
)
ORDER BY id;
-- 倒序 (最新在前) —— 本文重點優化
SELECT TOP @PageSize *
FROM table1
WHERE id <= (
SELECT ISNULL(MIN(id), (SELECT MAX(id) FROM table1))
FROM (SELECT TOP @PageSize * (@PageNum - 1) id FROM table1 ORDER BY id DESC) t
)
ORDER BY id DESC;
TOP 向前/向後掃描,IO 極少。ORDER BY id DESC 取前 N 頁的「最小 ID」作為上界,外層 WHERE id <= ... ORDER BY id DESC。SELECT * FROM (
SELECT ROW_NUMBER() OVER (ORDER BY id) AS rn, *
FROM table1
) t
WHERE rn > @PageSize * (@PageNum - 1) AND rn <= @PageSize * @PageNum;
OFFSET ... FETCH 語法簡化,執行計畫相同。| 方案 | 邏輯讀取 | CPU 時間 | 適用場景 |
|---|---|---|---|
| 方案二 (ID 邊界) | ~3 頁 | < 1ms | 單表、單調 PK、高併發分頁 |
| 方案三 (ROW_NUMBER) | ~5000 頁 | ~200ms | 多表 JOIN、複雜排序、非單調鍵 |
| 方案一 (NOT IN) | ~10000 頁 | ~500ms | 教學/極小資料量 |
前端傳遞 last_seen_id,後端 WHERE id < @last_seen_id ORDER BY id DESC LIMIT @PageSize。無頁碼跳轉、無深分頁效能衰減、天然支援無限滾動。