傳統 `DataSet`/`DataTable` 全量載入再分頁,資料量超過萬筆即嚴重拖慢記憶體與回應時間。真正的高效能分頁必須在 **資料庫層** 只取得當頁資料。
CREATE PROCEDURE dbo.Tag_Page_Name_Select
@MaxPageSize INT, -- 每頁筆數
@PageNum INT, -- 當前頁碼 (1-based)
@Count INT OUTPUT -- 總筆數
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Name NVARCHAR(50), @Cursor INT;
-- 1. 取得總筆數
SELECT @Count = COUNT(*) FROM [view_tag];
-- 2. 計算游標起始位置 (1-based)
SET @Cursor = @MaxPageSize * (@PageNum - 1) + 1;
-- 3. 處理頁碼超出範圍:自動導向最後一頁
IF @Cursor > @Count
BEGIN
IF @Count % @MaxPageSize = 0 -- 尾頁剛好滿頁
BEGIN
IF @Cursor > @MaxPageSize
SET @Cursor = @Count - @MaxPageSize + 1;
ELSE
SET @Cursor = 1;
END
ELSE -- 尾頁不滿
SET @Cursor = @Count - (@Count % @MaxPageSize) + 1;
END
-- 4. 移動游標至目標起始列
SET ROWCOUNT @Cursor;
SELECT @Name = tag_Name FROM [view_tag] ORDER BY tag_Name;
-- 5. 取得當頁資料
SET ROWCOUNT @MaxPageSize;
SELECT * FROM [view_tag] WHERE tag_Name >= @Name ORDER BY tag_Name;
-- 6. 重置 ROWCOUNT
SET ROWCOUNT 0;
END
CREATE PROCEDURE dbo.GetPagedData
@PageSize INT, @PageNumber INT, @TotalCount INT OUTPUT
AS
BEGIN
SELECT @TotalCount = COUNT(*) FROM LargeTable;
SELECT * FROM LargeTable
ORDER BY Id
OFFSET (@PageNumber - 1) * @PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY;
END
public class PagedResult
{
public List Items { get; set; }
public int TotalCount { get; set; }
public int PageSize { get; set; }
public int PageNumber { get; set; }
public int TotalPages => (int)Math.Ceiling((double)TotalCount / PageSize);
}
public PagedResult GetTags(int pageSize, int pageNumber)
{
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand("Tag_Page_Name_Select", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@MaxPageSize", pageSize);
cmd.Parameters.AddWithValue("@PageNum", pageNumber);
SqlParameter totalParam = cmd.Parameters.Add("@Count", SqlDbType.Int);
totalParam.Direction = ParameterDirection.Output;
conn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
var list = new List();
while (dr.Read())
{
list.Add(new Tag
{
Id = dr.GetInt32(0),
Name = dr.GetString(1)
});
}
return new PagedResult
{
Items = list,
TotalCount = (int)totalParam.Value,
PageSize = pageSize,
PageNumber = pageNumber
};
}
}
}
| 方案 | 資料量 10萬 | 資料量 100萬 | 記憶體 | 網路傳輸 |
|---|---|---|---|---|
| 全量載入 + PagedDataSource | ~2 秒 | OOM / >30 秒 | 高 (全表) | 全表 |
| 存儲過程分頁 (ROWCOUNT/OFFSET) | < 50ms | < 100ms | 極低 (單頁) | 單頁 |
高效能分頁核心在於 **資料庫層面只取需要的資料**。SQL Server 2012+ 請直接使用 `OFFSET/FETCH`;舊版本用 `ROWCOUNT` 游標法或 `ROW_NUMBER()` CTE。前端配合快取總頁數、Keyset 分頁,可支撐億級資料秒級翻頁。