分頁查詢是應用系統最常見的需求。本文介紹兩種經典實作:傳統 NOT IN 子查詢法(適用舊版本)與現代 ROW_NUMBER() 視窗函數法(SQL Server 2005+ 推薦),並提供可直接部署的預存程序範本。
StudentStudentNo, LoginPwd, StudentName, Sex, ClassId, Phone, Address, BornDate, Email, isDelSELECT TOP 5 * FROM StudentSELECT TOP 5 * FROM Student WHERE StudentNo NOT IN (SELECT TOP 10 StudentNo FROM Student)NOT IN 遇到 NULL 值會導致邏輯錯誤(結果為空集合)。SELECT ROW_NUMBER() OVER (ORDER BY StudentNo) AS RN, * FROM Student;
產生連續行號 RN,再以外層查詢篩選 RN BETWEEN (PageIndex-1)*PageSize+1 AND PageIndex*PageSize。
IF EXISTS (SELECT * FROM sysobjects WHERE name = 'usp_GetPageData')
DROP PROC usp_GetPageData;
GO
CREATE PROC usp_GetPageData
@toPage INT = 0 OUTPUT, -- 總頁數(輸出參數)
@pageIndex INT = 1, -- 目標頁碼(1-based)
@pageCount INT = 5 -- 每頁筆數
AS
BEGIN
SET NOCOUNT ON;
/* 1. 取得分頁資料 */
SELECT
temp.StudentNo, temp.LoginPwd, temp.StudentName, temp.Sex,
temp.ClassId, temp.Phone, temp.Address, temp.BornDate,
temp.Email, temp.isDel
FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY StudentNo) AS RN,
*
FROM Student
) temp
WHERE temp.RN > (@pageIndex - 1) * @pageCount
AND temp.RN <= @pageIndex * @pageCount;
/* 2. 計算總頁數(CEILING 需轉浮點避免整數除法截斷) */
SET @toPage = CEILING((SELECT COUNT(*) * 1.0 FROM Student) / @pageCount);
END
GO
DECLARE @totalPages INT; EXEC usp_GetPageData @toPage = @totalPages OUTPUT, @pageIndex = 3, @pageCount = 5; PRINT '總頁數:' + CAST(@totalPages AS VARCHAR);
ORDER BY 欄位建立索引,避免 Sort 運算子。WHERE PK > @lastSeenPK ORDER BY PK OFFSET 0 ROWS FETCH NEXT @pageSize ROWS ONLY,效能為 O(1)。UPDATE STATISTICS 確保 Cardinality Estimation 準確。| SQL Server 版本 | 推薦語法 |
|---|---|
| 2000 | NOT IN / 暫存表 + IDENTITY |
| 2005 ~ 2012 | ROW_NUMBER() + CTE/子查詢 |
| 2012+ | OFFSET ... FETCH NEXT(ANSI 標準、最簡潔) |
DECLARE @PageSize INT = 5, @PageIndex INT = 3; SELECT StudentNo, StudentName, Sex FROM Student ORDER BY StudentNo OFFSET (@PageIndex - 1) * @PageSize ROWS FETCH NEXT @PageSize ROWS ONLY;