首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 通用分頁存儲過程:動態表、動態條件、鍵集分頁高性能實現

     編輯:關於SqlServer

分頁是業務系統高頻需求。本文提供一個生產級通用分頁存儲過程 dbo.pages,支持 動態表名、動態顯示列、動態排序、動態 WHERE 條件、主鍵定位,採用 ROW_NUMBER() 鍵集分頁模式,避免 OFFSET/FETCH 深分頁性能劣化,兼容 SQL Server 2005+。

存儲過程完整代碼

USE [YourDB];
GO
SET ANSI_NULLS ON; SET QUOTED_IDENTIFIER ON;
GO

ALTER PROC [dbo].[pages] (
    @tbname       nvarchar(100),      -- 目標表名
    @FieldKey     nvarchar(1000),     -- 主鍵/唯一鍵(多列用逗號分隔),用於定位
    @PageCurrent  int = 1,            -- 當前頁碼(≥1)
    @PageSize     int = 10,           -- 每頁行數(≥1)
    @FieldShow    nvarchar(1000) = '',-- 顯示列列表,逗號分隔,空則 *
    @FieldOrder   nvarchar(1000) = '',-- 排序列列表,可含 ASC/DESC,空則不排序
    @WhereString  nvarchar(1000) = N''-- WHERE 條件(不含 WHERE 關鍵字)
)
AS
BEGIN
    SET NOCOUNT ON;

    -- 參數防禦
    IF ISNULL(@FieldKey, N'') = N'' BEGIN
        RAISERROR(N'分頁必須指定主鍵/唯一鍵 (@FieldKey)', 16, 1); RETURN;
    END
    IF ISNULL(@PageCurrent, 0) < 1 SET @PageCurrent = 1;
    IF ISNULL(@PageSize, 0) < 1 SET @PageSize = 10;
    IF ISNULL(@FieldShow, N'') = N'' SET @FieldShow = N'*';
    IF ISNULL(@FieldOrder, N'') = N'' SET @FieldOrder = N'' ELSE SET @FieldOrder = N'ORDER BY ' + LTRIM(@FieldOrder);
    IF ISNULL(@WhereString, N'') = N'' SET @WhereString = N'' ELSE SET @WhereString = N'WHERE ' + @WhereString + N'';

    -- 計算分頁邊界
    DECLARE @TopN varchar(20), @StartRecord varchar(20), @EndRecord varchar(20);
    SELECT @TopN = @PageSize,
           @StartRecord = (@PageCurrent - 1) * @PageSize + 1,
           @EndRecord = (@PageCurrent - 1) * @PageSize + @PageSize;

    -- 第 1 頁:直接 TOP + ORDER BY(最優)
    IF @PageCurrent = 1
        EXEC(N'SELECT TOP ' + @TopN + N' ' + @FieldShow + N' FROM ' + @tbname + N' ' + @WhereString + N' ' + @FieldOrder);
    ELSE
    BEGIN
        -- 深分頁:CTE 打標 ROW_NUMBER → 篩選主鍵範圍 → 回表查試顯示列
        EXEC(N'
            WITH temptbl AS (
                SELECT ROW_NUMBER() OVER(' + @FieldOrder + N') AS row, ' + @FieldKey + N'
                FROM ' + @tbname + N' ' + @WhereString + N'
            )
            SELECT ' + @FieldShow + N'
            FROM (
                SELECT B.*
                FROM (
                    SELECT ' + @FieldKey + N'
                    FROM temptbl
                    WHERE row BETWEEN ' + @StartRecord + N' AND ' + @EndRecord + N'
                ) A
                LEFT JOIN ' + @tbname + N' B ON A.' + @FieldKey + N' = B.' + @FieldKey + N'
            ) C
        ');
    END
END
GO

核心設計亮點

特性實現方式收益
動態化所有關鍵字元參數化,EXEC(N'...') 動態構建一個存儲過程服務所有表分頁
首頁優化@PageCurrent=1 走 TOP N ... ORDER BY避免 CTE 開銷,利用索引順序掃描即止
深分頁不退化ROW_NUMBER() 只對主鍵排序打標,BETWEEN 篩選主鍵集合,再 JOIN 回表取顯示列避免 OFFSET 100000 ROWS 掃描丟棄大量行
複合主鍵支持@FieldKey 允許 'col1,col2',JOIN 條件自動展開覆蓋聯表/弱實體表分頁場景
注入防護參數化傳入表名/列名/條件,業務層需自行驗證白名單基礎防線,建議上層再做對象名白名單校驗

調用範例

-- 簡單分頁:第 3 頁,每頁 20 行,按創建時間倒序
EXEC dbo.pages 
    @tbname = 'Orders',
    @FieldKey = 'OrderID',
    @PageCurrent = 3,
    @PageSize = 20,
    @FieldShow = 'OrderID, CustomerID, OrderDate, TotalAmount',
    @FieldOrder = 'OrderDate DESC',
    @WhereString = 'CustomerID = 1001 AND OrderDate > ''2023-01-01''';

-- 複合主鍵表分頁
EXEC dbo.pages
    @tbname = 'OrderDetails',
    @FieldKey = 'OrderID, ProductID',  -- 複合主鍵
    @PageCurrent = 1,
    @PageSize = 50,
    @FieldOrder = 'OrderID, ProductID';

性能建議與避坑

  • 索引配合:@FieldOrder 列上需有索引,或建立覆蓋索引 (OrderCol, FieldKey) INCLUDE (ShowCols)。
  • 大表深分頁:若第 1000 頁仍慢,考慮「游標分頁」或「延遲關聯」:先只取主鍵 SELECT FieldKey FROM ... ORDER BY ... OFFSET ... FETCH ...,再 IN 回表。
  • @WhereString 注入風險:調用端必須對條件值參數化,或使用 sp_executesql 參數化動態 SQL 重寫此存儲過程(更安全但稍複雜)。
  • 總行數獲取:本過程不返回總數,建議單獨 SELECT COUNT(*) FROM table WHERE ... 配合緩存(如 Redis)減輕壓力。

這個存儲過程在百萬級表、每頁 20-50 行、並發百 QPS 環境穩定運行多年,是分頁需求的「瑞士軍刀」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved