CREATE TABLE TestTable (
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
FirstName NVARCHAR(100) NULL,
LastName NVARCHAR(100) NULL,
Country NVARCHAR(50) NULL,
Note NVARCHAR(2000) NULL
);
SET IDENTITY_INSERT TestTable ON;
DECLARE @i INT = 1;
WHILE @i <= 20000
BEGIN
INSERT INTO TestTable (ID, FirstName, LastName, Country, Note)
VALUES (@i, 'FirstName_XXX', 'LastName_XXX', 'Country_XXX', 'Note_XXX');
SET @i += 1;
END
SET IDENTITY_INSERT TestTable OFF;
SELECT TOP 10 *
FROM TestTable
WHERE ID NOT IN (
SELECT TOP 20 ID FROM TestTable ORDER BY ID
)
ORDER BY ID;
通用寫法:WHERE ID NOT IN (SELECT TOP (頁大小*頁數) ID FROM 表 ORDER BY ID)
缺點: NOT IN 在大量資料下效能差,且若子查詢含 NULL 會導致邏輯錯誤。
SELECT TOP 10 *
FROM TestTable
WHERE ID > (
SELECT MAX(ID) FROM (
SELECT TOP 20 ID FROM TestTable ORDER BY ID
) AS T
)
ORDER BY ID;
通用寫法:WHERE ID > (SELECT MAX(ID) FROM (SELECT TOP (頁大小*頁數) ID FROM 表 ORDER BY ID) AS T)
優點: 善用叢集索引搜尋,避免 NOT IN 掃描,效能最佳。
CREATE PROCEDURE XiaoZhengGe
@sqlstr NVARCHAR(4000),
@currentpage INT,
@pagesize INT
AS
SET NOCOUNT ON;
DECLARE @P1 INT, @rowcount INT;
EXEC sp_cursoropen @P1 OUTPUT, @sqlstr, @scrollopt=1, @ccopt=1, @rowcount=@rowcount OUTPUT;
SELECT CEILING(1.0*@rowcount/@pagesize) AS 總頁數;
SET @currentpage = (@currentpage-1)*@pagesize + 1;
EXEC sp_cursorfetch @P1, 16, @currentpage, @pagesize;
EXEC sp_cursorclose @P1;
SET NOCOUNT OFF;
優點: 通用性高,無需主鍵、支援任意複雜查詢。
缺點: 效能最差,游標開銷大,不建議高併發場景使用。
| 方案 | 邏輯讀取 | CPU 時間 | 適用場景 |
|---|---|---|---|
| 方案二 (ID > MAX) | 最低 | 最低 | 大量資料、有序主鍵、高併發 |
| 方案一 (NOT IN) | 中等 | 中等 | 小資料量、無主鍵或無法使用 ID 範圍 |
| 方案三 (游標) | 最高 | 最高 | 臨時/通用/無主鍵查詢,低頻呼叫 |
SELECT * FROM TestTable
ORDER BY ID
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
此為 ANSI 標準語法,計畫穩定、可讀性佳,升級後請優先採用。
SELECT *,僅取必要欄位減少 I/O。COUNT(*) 或使用近似值。