CREATE DATABASE DBTest; USE DBTest;
CREATE TABLE pagetest (
id INT IDENTITY(1,1) NOT NULL,
col01 INT NULL, col02 NVARCHAR(50) NULL, col03 DATETIME NULL
);
-- 插入 1 萬筆測試資料
DECLARE @i INT = 0;
WHILE @i < 10000 BEGIN
INSERT pagetest VALUES (CAST(RAND()*10000 AS INT), LEFT(NEWID(),10), GETDATE());
SET @i = @i + 1;
END
測試場景:每頁 50 筆,查詢第 199 頁(9901-9950)。
SELECT TOP 50 * FROM pagetest
WHERE id NOT IN (SELECT TOP 9900 id FROM pagetest ORDER BY id)
ORDER BY id;
SELECT TOP 50 * FROM pagetest
WHERE NOT EXISTS (
SELECT 1 FROM (SELECT TOP 9900 id FROM pagetest ORDER BY id) a
WHERE a.id = pagetest.id
) ORDER BY id;
SELECT TOP 50 * FROM pagetest
WHERE id > (SELECT MAX(id) FROM (SELECT TOP 9900 id FROM pagetest ORDER BY id) a)
ORDER BY id;
-- 4.1 子查詢過濾
SELECT TOP 50 * FROM (
SELECT ROW_NUMBER() OVER(ORDER BY id) rownumber, * FROM pagetest
) a WHERE rownumber > 9900;
-- 4.2 BETWEEN 範圍
SELECT * FROM (
SELECT ROW_NUMBER() OVER(ORDER BY id) rownumber, * FROM pagetest
) a WHERE rownumber BETWEEN 9901 AND 9950;
-- 4.3 雙重條件
SELECT * FROM (
SELECT ROW_NUMBER() OVER(ORDER BY id) rownumber, * FROM pagetest
) a WHERE rownumber > 9900 AND rownumber < 9951;
SELECT * FROM (
SELECT ROW_NUMBER() OVER(ORDER BY tempColumn) rownumber, * FROM (
SELECT TOP 9950 tempColumn = 0, * FROM pagetest WHERE 1=1 ORDER BY id
) a
) b WHERE rownumber > 9900;

| 寫法 | 相對效能 | 可移植性 | 支援複雜排序 | 推薦等級 |
|---|---|---|---|---|
| MAX/TOP | ★★★★★ | SQL 2000/Access 皆可 | 單欄位遞增鍵 | 舊版首選 |
| ROW_NUMBER() | ★★★★★ | SQL 2005+ | 任意複雜排序 | 現代首選 |
| NOT EXISTS | ★★★★☆ | 廣泛 | 一般 | 備選 |
| NOT IN | ★★★☆☆ | 廣泛 | 差 | 不推薦 |
| ROW_NUMBER 變體 | ★★☆☆☆ | SQL 2005+ | 一般 | 不推薦 |
ALTER PROCEDURE Proc_SqlPageByRownumber
@tbName VARCHAR(255), @tbGetFields VARCHAR(1000) = '*',
@OrderfldName VARCHAR(255), @PageSize INT = 20, @PageIndex INT = 1,
@OrderType BIT = 0, @strWhere VARCHAR(1000) = '', @TotalCount INT OUTPUT
AS
BEGIN
DECLARE @strOrder VARCHAR(300) = ' ORDER BY ' + @OrderfldName +
CASE WHEN @OrderType <> 0 THEN ' DESC' ELSE ' ASC' END;
-- 總筆數
DECLARE @strSqlCount NVARCHAR(500) = 'SELECT @TotalCout=COUNT(*) FROM ' + @tbName +
CASE WHEN @strWhere <> '' THEN ' WHERE 1=1 ' + @strWhere ELSE '' END;
EXEC sp_executesql @strSqlCount, N'@TotalCout INT OUTPUT', @TotalCount OUTPUT;
-- 分頁查詢
DECLARE @strSql VARCHAR(5000) = 'SELECT * FROM (SELECT ROW_NUMBER() OVER(' + @strOrder +
') RowNo, ' + @tbGetFields + ' FROM ' + @tbName + ' WHERE 1=1 ' + @strWhere +
') tb WHERE tb.RowNo BETWEEN ' + STR((@PageIndex-1)*@PageSize+1) + ' AND ' + STR(@PageIndex*@PageSize);
EXEC(@strSql);
SELECT @TotalCount;
END
@tbName = "UserInfo u INNER JOIN Department d ON u.DepID = d.ID"
@tbGetFields = "u.ID AS UserID, u.Name, u.Sex, d.ID AS DepID, d.DeptName"
@OrderfldName = "u.ID,ASC|u.Name,DESC" -- 格式:欄位,排序|欄位,排序
@strWhere = "AND u.DepID = 1 AND u.Status = 1" -- 每條件前加 AND
CREATE PROC spSqlPageByMaxTop
@tbName VARCHAR(255), @tbFields VARCHAR(1000), @PageSize INT, @PageIndex INT,
@strWhere VARCHAR(1000), @StrOrder VARCHAR(255), @Total INT OUTPUT
AS
-- 總筆數
DECLARE @strSqlCount NVARCHAR(500) = 'SELECT @TotalCout=COUNT(*) FROM ' + @tbName +
CASE WHEN @strWhere <> '' THEN ' WHERE ' + @strWhere ELSE '' END;
EXEC sp_executesql @strSqlCount, N'@TotalCout INT OUTPUT', @Total OUTPUT;
-- 分頁
DECLARE @strSql VARCHAR(5000) = 'SELECT TOP ' + STR(@PageSize) + ' * FROM ' + @tbName +
' WHERE id > (SELECT MAX(id) FROM (SELECT TOP ' + STR((@PageIndex-1)*@PageSize) +
' id FROM ' + @tbName + ' ' + @StrOrder + ') a) ' + @StrOrder;
EXEC(@strSql);
SQL Server 2012+ 直接使用 OFFSET-FETCH,無需動態 SQL、效能最優、語法標準:
SELECT * FROM pagetest
ORDER BY id
OFFSET (@PageIndex-1)*@PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY;