SQL存储过程批量更新多任务ID及写入日志功能异常求助
问题根因
- 循环内直接执行
RETURN语句:无论第一个ID处理成功还是失败,RETURN会直接终止整个存储过程,永远不会进入下一次循环,这是只处理第一个ID的核心原因。 - 未更新待拆分字符串:每次拆分完第一个ID后,没有将已处理部分从
@StringToSplit中移除,CHARINDEX每次都定位到第一个逗号的位置,永远在处理同一个ID,逻辑死循环。 - 未处理入参的前后括号:前端传入的ID格式是
[12,13,14],直接拆分的话第一个ID会是[12,转换int类型时直接报错,导致存储过程异常终止。 - 遗漏最后一个ID:循环判断条件是存在逗号才执行,最后一个ID后面没有逗号,会被遗漏处理。
修复方案
方案1:兼容低版本SQL Server的循环修复版
调整后的代码修复了上述所有问题,将返回逻辑移到循环结束后,增加括号处理、字符串截断、最后一个ID处理逻辑:
ALTER PROCEDURE [dbo].[ur_tasks_confirm_order] @user as varchar(50), @StringToSplit as varchar(max) AS BEGIN SET NOCOUNT ON; DECLARE @TaskID as int, @pos as int, @ResultCode int = 0 -- 默认成功 -- 第一步:先去掉传入字符串的前后括号 SET @StringToSplit = REPLACE(REPLACE(@StringToSplit,'[',''),']','') While CHARINDEX(',',@StringToSplit) > 0 BEGIN SELECT @pos = CHARINDEX(',',@StringToSplit) SELECT @TaskID = cast(SUBSTRING(@StringToSplit,1,@pos-1) as int) -- 更新任务状态 UPDATE ur_tasks_details SET StatusOfOrder = 4 WHERE ID = @TaskID IF @@Rowcount = 1 BEGIN DECLARE @OrderNumber as int SELECT @OrderNumber = OrderNumber FROM ur_tasks_details WHERE ID = @TaskID INSERT INTO ur_tasks_logs VALUES (@TaskID,@OrderNumber,4,@user,Getdate()) IF @@Rowcount <> 1 SET @ResultCode = 5 -- 日志写入失败标记 END ELSE BEGIN SET @ResultCode = 4 -- 任务更新失败标记 END -- 截断已处理的部分,进入下一个ID处理 SET @StringToSplit = SUBSTRING(@StringToSplit, @pos+1, LEN(@StringToSplit)-@pos) END -- 处理最后一个没有逗号的ID IF LEN(@StringToSplit) > 0 BEGIN SELECT @TaskID = cast(@StringToSplit as int) UPDATE ur_tasks_details SET StatusOfOrder = 4 WHERE ID = @TaskID IF @@Rowcount = 1 BEGIN DECLARE @OrderNumberLast as int SELECT @OrderNumberLast = OrderNumber FROM ur_tasks_details WHERE ID = @TaskID INSERT INTO ur_tasks_logs VALUES (@TaskID,@OrderNumberLast,4,@user,Getdate()) IF @@Rowcount <> 1 SET @ResultCode = 5 END ELSE BEGIN SET @ResultCode = 4 END END -- 所有ID处理完再统一返回结果 RETURN @ResultCode END
方案2:SQL Server 2016及以上版本推荐(无循环,高性能)
使用系统内置的STRING_SPLIT函数实现批量处理,无需手写循环,性能远高于逐行处理,代码更简洁易维护:
ALTER PROCEDURE [dbo].[ur_tasks_confirm_order] @user as varchar(50), @StringToSplit as varchar(max) AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; -- 出错自动回滚事务 -- 先清理括号,拆分ID为临时表 DECLARE @TaskIDs TABLE (ID int) INSERT INTO @TaskIDs(ID) SELECT CAST(value as int) FROM STRING_SPLIT(REPLACE(REPLACE(@StringToSplit,'[',''),']',''), ',') WHERE ISNUMERIC(value) = 1 -- 过滤非法ID格式 BEGIN TRY BEGIN TRANSACTION -- 批量更新所有任务状态 UPDATE ur_tasks_details SET StatusOfOrder = 4 WHERE ID IN (SELECT ID FROM @TaskIDs) -- 批量写入日志 INSERT INTO ur_tasks_logs(TaskID, OrderNumber, Status, OperateUser, OperateTime) SELECT t.ID, t.OrderNumber, 4, @user, GETDATE() FROM ur_tasks_details t INNER JOIN @TaskIDs ids ON t.ID = ids.ID WHERE t.StatusOfOrder = 4 -- 确保只写入更新成功的记录 COMMIT TRANSACTION RETURN 0 -- 全部处理成功 END TRY BEGIN CATCH ROLLBACK TRANSACTION IF ERROR_NUMBER() = 515 -- 空值错误等可以按需细分错误码 RETURN 5 ELSE RETURN 4 END CATCH END
内容的提问来源于stack exchange,提问作者neider
相关产品推荐
相关产品推荐

