在處理大量數據的應用場景中,分頁查詢是核心需求之一。本文介紹一個經過實戰驗證的通用分頁存儲過程 MyPagination,它能夠高效支撐千萬級數據的分頁顯示,並提供靈活的參數配置。
該存儲過程採用動態 SQL 構建方式,根據頁碼採取不同的查詢策略:
@PageIndex = 1 時,直接使用 TOP 子句查詢,避免臨時表開銷,大幅提升首頁加載速度。IDENTITY 函數為結果集生成行號,存入臨時表 #tmpTable,再根據行號篩選目標頁數據。@doCount 控制是否執行 COUNT(*),統計結果暫存於臨時表 tmpTable 後讀取並銷毀。| 參數名 | 類型 | 默認值 | 說明 |
|---|---|---|---|
| @tblName | varchar(255) | - | 目標表名 |
| @strGetFields | varchar(1000) | * | 需返回的列,'*' 表示所有列;若包含自增列需用 (fldName + 0) AS fldName 處理 |
| @PageSize | int | 10 | 每頁記錄數 |
| @PageIndex | int | 1 | 頁碼(從 1 開始) |
| @doCount | bit | 0 | 非 0 時返回總記錄數於輸出參數 @RecordCount |
| @strOrderBy | varchar(500) | '' | 排序字段,格式:Field1 DESC, Field2 ASC(不含 ORDER BY) |
| @strWhere | varchar(1500) | '' | 查詢條件(不含 WHERE) |
| @RecordCount | int output | - | 輸出參數,返回總記錄數 |
-- 總記錄數統計
IF (@doCount != 0)
BEGIN
DECLARE @sWhere varchar(2000) = ''
IF (@strWhere != '') SET @sWhere = ' WHERE ' + @strWhere
SET @strSQL = 'if exists (select * from dbo.sysobjects where id = object_id(''[dbo].[tmpTable]'') and OBJECTPROPERTY(id, ''IsUserTable'') = 1) '
SET @strSQL = @strSQL + ' UPDATE tmpTable SET Total = (SELECT COUNT(*) FROM [' + @tblName + '] ' + @sWhere + ') '
SET @strSQL = @strSQL + ' ELSE SELECT COUNT(*) AS Total INTO tmpTable FROM [' + @tblName + '] ' + @sWhere
EXEC (@strSQL)
SELECT @RecordCount=Total FROM tmpTable
EXEC ('DROP TABLE tmpTable')
END
-- 構建排序子句
IF (@strOrderBy != '') SET @strOrder = ' ORDER BY ' + @strOrderBy
-- 首頁直接 TOP 查詢
IF (@PageIndex = 1)
BEGIN
IF (@strWhere != '')
SET @strSQL = 'SELECT TOP ' + str(@PageSize) + ' ' + @strGetFields + ' FROM [' + @tblName + '] WHERE ' + @strWhere + @strOrder
ELSE
SET @strSQL = 'SELECT TOP ' + str(@PageSize) + ' ' + @strGetFields + ' FROM ['+ @tblName + '] '+ @strOrder
END
ELSE
BEGIN
-- 非首頁:生成帶行號的臨時表
SET @strSQL = 'SELECT TOP ' + str(@PageIndex*@PageSize) + ' IDENTITY(int,1,1) AS IID, ' + @strGetFields + ' INTO #tmpTable FROM [' + @tblName + ']'
IF (@strWhere != '') SET @strSQL = @strSQL + ' WHERE ' + @strWhere + @strOrder
ELSE SET @strSQL = @strSQL + @strOrder
-- 從臨時表篩選目標頁
SET @strSQL = @strSQL + ' SELECT ' + @strGetFields + ' FROM #tmpTable WHERE IID > ' + str((@PageIndex-1)*@PageSize) + ' DROP TABLE #tmpTable'
END
EXEC (@strSQL)
DECLARE @TotalCount INT
EXEC MyPagination
@tblName = 'Orders',
@strGetFields = 'OrderID, CustomerID, OrderDate, TotalAmount',
@PageSize = 20,
@PageIndex = 3,
@doCount = 1,
@strOrderBy = 'OrderDate DESC',
@strWhere = 'Status = 1',
@RecordCount = @TotalCount OUTPUT
SELECT @TotalCount AS TotalRecords
@strOrderBy 欄位存在索引,否則大數據量下排序會成為瓶頸。@strWhere 中使用導致索引失效的寫法(如函數包裹列、前置通配符 LIKE '%...')。MyPagination 通過首頁直取、非首頁行號法的混合策略,在兼顧通用性的前提下實現了不錯的性能表現。對於千萬級數據的常規分頁場景,它是一個開箱即用的可靠方案。隨著 SQL Server 版本迭代,也可考慮遷移至原生 OFFSET ... FETCH 語法以簡化維護。