SQL Server 无#TempTable存储过程实现返回3结果集分页查询
实现结论
完全可以通过逻辑轻量化的存储过程满足需求,全程不需要创建临时表,只需要在动态SQL中按顺序拼接3段独立的SELECT语句,即可一次性返回要求的3个结果集,和现有客户端接多结果集的逻辑完全兼容。
先指出你现有动态SQL的两个实际跑会踩的坑:
- 变量判断拼写错误:原代码判断的是
@Nome变量,但实际声明的是@Name,会导致姓名字段的筛选条件完全不生效- 硬拼参数的风险:所有条件值直接拼进SQL字符串,遇到带单引号的文本值会直接执行失败,还存在SQL注入隐患,用
sp_executesql的参数传参机制可以彻底解决这个问题
核心实现思路
对应要求的3个返回结果集,存储过程内按执行顺序输出即可,完全不需要依赖临时表:
- 第一结果集:带多条件筛选的分页业务数据,用SQL原生的
OFFSET ... ROWS FETCH NEXT ... ROWS ONLY实现分页逻辑 - 第二结果集:错误标识的布尔值,直接输出固定值
CAST(0 AS BIT) AS HasError即可,不需要关联任何业务表,后续如果需要加错误捕获,直接把这个值改成变量就行 - 第三结果集:匹配筛选条件的总数据量,和业务查询复用完全一致的WHERE条件,执行
COUNT(1)得到分页需要的总条数
可直接复用的存储过程示例
下面的实现把公共WHERE条件提取成了一段,避免业务查询和计数查询的筛选逻辑出现不一致的问题:
CREATE OR ALTER PROCEDURE sp_GetClientEstimateList -- 业务筛选入参 @SellerCode INT, @Year INT = NULL, @Name VARCHAR(100) = NULL, @Document VARCHAR(100) = NULL, @GroupCode INT = NULL, @UserId INT = NULL, -- 分页入参 @PageSize INT = 10, @PageNumber INT = 1 AS BEGIN DECLARE @DynamicSQL NVARCHAR(MAX), @ParameterDefinition NVARCHAR(MAX); -- 定义动态SQL可接收的参数,避免硬拼字符串 SET @ParameterDefinition = N' @SellerCode INT, @Year INT, @Name VARCHAR(100), @Document VARCHAR(100), @GroupCode INT, @UserId INT, @PageSize INT, @PageNumber INT '; -- 拼接完整SQL逻辑 SET @DynamicSQL = N' -- 提取公共WHERE条件,业务查询、计数查询共用 DECLARE @BaseWhere NVARCHAR(MAX) = N'' FROM Estimate INNER JOIN Client ON Estimate.ClientId = Client.ClientId INNER JOIN Seller ON Client.SellerCode = Seller.SellerCode WHERE Client.SellerCode = @SellerCode ''; -- 追加动态筛选分支 IF @Name IS NOT NULL SET @BaseWhere += N'' AND Client.Name LIKE ''%'' + @Name + ''%''; IF @Document IS NOT NULL SET @BaseWhere += N'' AND Estimate.Document = @Document; IF @GroupCode IS NOT NULL SET @BaseWhere += N'' AND Client.GroupCode = @GroupCode; IF @Year IS NOT NULL SET @BaseWhere += N'' AND Estimate.Year = @Year; IF @UserId IS NOT NULL SET @BaseWhere += N'' AND Client.UserId = @UserId; -- 1. 第一返回结果集:分页后的业务数据 DECLARE @OffsetValue INT = (@PageNumber - 1) * @PageSize; EXEC(N'' SELECT Client.Id, Client.Document, Client.Name, Client.Active, Client.Email, Client.GroupCode, Client.UserId, Estimate.Paid, Estimate.Value, Estimate.Year '' + @BaseWhere + N'' ORDER BY Client.Id OFFSET '' + CAST(@OffsetValue AS VARCHAR(20)) + ROWS FETCH NEXT '' + CAST(@PageSize AS VARCHAR(20)) + ROWS ONLY ''); -- 2. 第二返回结果集:执行错误标识(默认无错误) SELECT CAST(0 AS BIT) AS HasError; -- 3. 第三返回结果集:分页用总数据量 EXEC(N''SELECT COUNT(1) AS TotalCount '' + @BaseWhere); '; -- 执行动态SQL,传入所有参数 EXEC sys.sp_executesql @DynamicSQL, @ParameterDefinition, @SellerCode = @SellerCode, @Year = @Year, @Name = @Name, @Document = @Document, @GroupCode = @GroupCode, @UserId = @UserId, @PageSize = @PageSize, @PageNumber = @PageNumber; END
实现优势说明
- 全程没有创建任何用户临时表,所有逻辑基于内存中的SQL字符串拼接和原生SELECT输出,执行开销远低于带临时表的实现
- 公共筛选条件只维护一遍,不会出现业务查询和计数查询的筛选规则偏差
- 三个结果集固定顺序返回,前端按顺序读取即可:第一块取业务列表、第二块取错误状态、第三块取分页总条数,不需要额外关联字段
- 全参数化传参,彻底避免SQL注入和特殊字符导致的执行报错
内容的提问来源于stack exchange,提问作者DValdir Martins
相关产品推荐
相关产品推荐

