分頁是 MIS/Web 系統核心需求,一次性拉取全量數據會撐爆內存與網絡。各數據庫方言差異大,本文匯總 SQL Server、Oracle、MySQL、PostgreSQL 四大主流庫的標準分頁寫法,並給出生產環境優化建議。
SELECT col1, col2
FROM dbo.TableName
ORDER BY PK_Column
OFFSET @PageSize * (@PageNum - 1) ROWS
FETCH NEXT @PageSize ROWS ONLY;
OFFSET/FETCH 符合 ANSI SQL:2008,計畫穩定、可讀性強。ORDER BY 且順序唯一(建議含 PK),否則結果不確定。WITH CTE AS (
SELECT col1, col2,
ROW_NUMBER() OVER (ORDER BY PK_Column) AS rn
FROM dbo.TableName
)
SELECT col1, col2
FROM CTE
WHERE rn BETWEEN @StartRow AND @EndRow;
SELECT *
FROM (
SELECT TOP @PageSize *
FROM (
SELECT TOP (@PageSize * @PageNum) *
FROM TableName
ORDER BY PK_Column DESC
) t1
ORDER BY PK_Column ASC
) t2;
SELECT col1, col2
FROM TableName
ORDER BY PK_Column
OFFSET :offset ROWS FETCH NEXT :pageSize ROWS ONLY;
SELECT *
FROM (
SELECT ROWNUM rn, t1.*
FROM (
SELECT col1, col2
FROM TableName
ORDER BY PK_Column
) t1
WHERE ROWNUM <= :endRow
) t2
WHERE t2.rn >= :startRow;
關鍵點:內層 ORDER BY 必須在 ROWNUM 過濾之前,否則順序錯亂。
SELECT col1, col2
FROM TableName
ORDER BY PK_Column
LIMIT @offset, @pageSize; -- 或 LIMIT @pageSize OFFSET @offset
LIMIT M, N:跳過 M 行,取 N 行(M 從 0 開始)。SELECT col1, col2
FROM TableName
ORDER BY PK_Column
LIMIT @pageSize OFFSET @offset;
FETCH FIRST @pageSize ROWS ONLY 標準語法。| 策略 | 原理 | 適用場景 | 代碼示例 |
|---|---|---|---|
| Keyset Pagination (Seek Method) | 記住上一頁最大 PK,下頁 WHERE pk > @lastPk ORDER BY pk LIMIT N |
單向翻頁(無跳頁需求),PK 單調遞增 | SELECT * FROM t WHERE id > 1000 ORDER BY id LIMIT 20; |
| 延遲關聯 | 先只查 PK 分頁,再 JOIN 回表取明細 |
寬表、多列查詢、覆蓋索引無法覆蓋所有列 | SELECT t.* FROM t JOIN (SELECT id FROM t ORDER BY id LIMIT 20 OFFSET 10000) tmp USING(id); |
| 覆蓋索引 | 索引包含所有 SELECT 列,避免回表 | 高頻分頁查詢,列數較少 | CREATE INDEX idx_cov ON t (pk) INCLUDE (col1, col2); |
| 緩存首頁/熱頁 | Redis 緩存前 N 頁 JSON,直讀返回 | 首頁、熱門列表、實時性要求秒級 | — |
EXPLAIN 驗證 Using index。COUNT(*) 走覆蓋索引或緩存,避免全表掃描。