@PageSize:每頁筆數@PageIndex:頁碼 (1-based)@StartRow = (@PageIndex-1)*@PageSize + 1@EndRow = @PageIndex*@PageSizeSELECT TOP (@PageSize) * FROM tbl WHERE id NOT IN (SELECT TOP (@PageSize * @PageIndex) id FROM tbl ORDER BY id) ORDER BY id;
NOT IN 遇 `NULL` 還有語義陷阱。SELECT IDENTITY(INT, 1, 1) AS RowNum, * INTO #tmp FROM tbl ORDER BY id; SELECT * FROM #tmp WHERE RowNum BETWEEN @StartRow AND @EndRow; DROP TABLE #tmp;
SELECT TOP (@PageSize) *
FROM (
SELECT TOP (@PageSize * @PageIndex) *
FROM tbl
ORDER BY id ASC
) AS t
ORDER BY id DESC;
WITH CTE AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS RN FROM tbl
)
SELECT * FROM CTE WHERE RN BETWEEN @StartRow AND @EndRow;
SELECT * FROM tbl ORDER BY id OFFSET (@PageIndex-1)*@PageSize ROWS FETCH NEXT @PageSize ROWS ONLY;
| 寫法 | 邏輯讀取 | CPU 時間 | 適用版本 | 推薦等級 |
|---|---|---|---|---|
| TOP + NOT IN | 極高 | 極高 | All | ⭐ (不推薦) |
| 暫存表 | 中 (含寫入) | 中 | All | ⭐⭐ |
| 巢狀 TOP | 低~中 | 低~中 | All | ⭐⭐⭐ |
| ROW_NUMBER() | 低 | 低 | 2005+ | ⭐⭐⭐⭐ |
| OFFSET-FETCH | 最低 | 最低 | 2012+ | ⭐⭐⭐⭐⭐ |
SELECT t.* FROM tbl t JOIN (SELECT id FROM tbl ORDER BY id OFFSET 1000000 ROWS FETCH NEXT 20 ROWS ONLY) p ON t.id = p.id;
**SQL 2012+ 請直接用 `OFFSET-FETCH`**;舊版用 `ROW_NUMBER()`。避免 `NOT IN`、`tempdb` 方案。深分頁採用「延遲關聯」或「游標式」設計,從根源消除大量掃描。