SQL Server 尽管第一步失败,但存储过程仍在运行

ou6hu8tu  于 2023-02-15  发布在  其他
关注(0)|答案(1)|浏览(143)

我正在对一个存储过程进行故障排除,该存储过程应该将数据从一个SQL Server示例发送到Azure上托管的另一个SQL Server示例。
存储过程在研究它之后的工作方式是,它首先根据参数计算出它要传输哪些表,所以我最后得到一个表变量@transferTable,如下所示

id     tableName
===================
23     tableName1
55     tableName23
72     tableName111

在一个循环中,外观设置为

SELECT @recordID = MIN(id) FROM @transferTable

WHILE @recordID IS NOT NULL
BEGIN
-- Update attempt
    UPDATE transferlog
    SET LastAttempt = GetDate()
    WHERE id = @recordID;
-- attempt
   SET @SQLCommand 'Insert into Azure.dbo.table blah blah' -- this part is broken right now
   print 'about to run sql command' --this prints
   Execute (@SQL Command)
   print 'ran sql' --this does not print
-- Update success
   UPDATE transferlog
   SET LastUpdate = GetDate()
   WHERE id=@recordID;
SELECT @recordID = MIN(id) FROM @transferTable WHERE id > @recordID
END

这是这个存储过程如何工作的草图。现在它在第一个表上失败了,我知道原因-Azure版本的表上有一个NOT NULL约束,它试图插入的一个记录中有一个NULL值。这对我来说是有意义的。
没有意义的是,传输日志表不仅更新了列中包含有问题的NULL值的第一个表的LastAttemptLastUpdate列,而且还更新了其后所有表的LastAttemptLastUpdate列。
为什么会发生这种情况?虽然存储过程的作者预期LastUpdate会在且仅在未发生错误的情况下运行,但似乎并非如此。如何确保LastUpdated列实际上仅在插入Azure SQL Server数据库时未发生错误的情况下更新?

qvtsj1bj

qvtsj1bj1#

SQL Server中的错误并不都是“相同的”,有些错误不会导致批处理中止,延迟批处理中的错误,通过使用EXEC (@SQL)或(更好的)sys.sp_executesql,不会导致外部批处理结束(这实际上也是经常需要的行为),更不用说还有一些“批处理前”中止,因为它们发生在批处理被解析的时候。
取以下批次:

SELECT 1;
EXEC sys.sp_executesql N'SELECT 1 / 0;';
SELECT 2;
EXEC sys.sp_executesql N'SELECT * FROM dbo.DoesNotExist;';
SELECT 3;

这仍将导致返回包含23的结果集,尽管这两个语句都在延迟批处理错误中执行。
实际上,如果我们将这些语句移出延迟批处理,那么您 * 仍然 * 得到12

SELECT 1;
SELECT 1 / 0;
SELECT 2;
SELECT * FROM dbo.DoesNotExist;
SELECT 3;

但是请注意,3不会返回,因为引用一个不存在的对象会导致同一范围内的批处理中止错误。
如果您想确保批处理总是被中止,那么一个方法是启用XACT_ABORT

SET XACT_ABORT ON;
SELECT 1;
EXEC sys.sp_executesql N'SELECT 1 / 0;';
SELECT 2;
EXEC sys.sp_executesql N'SELECT * FROM dbo.DoesNotExist;';
SELECT 3;

这只会导致返回1,您可能还需要确保您的BEGINCOMMITTRANSACTION,以便在出现故障时“自动”回滚任何内容。
或者,您可以使用BEGIN TRY... CATCH,但是,我并没有真正看到它的好处:

BEGIN TRY
    SELECT 1;
    EXEC sys.sp_executesql N'SELECT 1 / 0;';
    SELECT 2;
    EXEC sys.sp_executesql N'SELECT * FROM dbo.DoesNotExist;';
    SELECT 3;
END TRY
BEGIN CATCH
    THROW;
END CATCH;

相关问题