我有一个执行以下操作的存储过程:
BEGIN TRANSACTION
-- Code to delete updated records from production (dbo) table
DELETE FROM [dbo].[factMyTable]
WHERE exists (SELECT *
FROM [RAW].[MyTable]
WHERE [RAW].[MyTable].[refno] = [dbo].[factMyTable].[refno]
AND [RAW].[MyTable].[modification_dttm] >= [dbo].[factMyTable].[modification_dttm]
)
-- Code to perform the append of incremental records
INSERT INTO [dbo].[factMyTable]
SELECT
[refno]
,[field1]
,[field2]
,[field3]
,[FieldN]
,[modification_dttm]
FROM [RAW].[MyTable]
-- Truncate stage table and get ready for next load
TRUNCATE TABLE [RAW].[MyTable]
COMMIT TRANSACTION
正如您在上面看到的,我有一个包含在 BEGIN/COMMIT 事务块中的截断命令。但是,在插入命令中执行此存储过程时出现错误,其中设置为 NOT NULL 的字段正在接收 NULL 值。因此:
- 将 RAW 表中的记录插入 dbo 表的操作被回滚;但
- RAW 表的截断没有回滚。
这个想法是,如果插入数据时出现错误,则不应发生截断。
根据这篇文章,我们可以回滚 truncate 命令,但也许我的存储过程没有正确编写脚本。也许有一种更直接的方法可以确保只有在插入没有返回错误时才会发生截断?我该怎么办?
不,根本没有
rollback
,这是复制品。这
xact_abort off
是您default
设置的选项,我创建了 2 个表,然后我打开事务结束执行 2inserts
个,其中一个(第二个)失败,我从两个表中添加select @@trancount
,select
以便您可以更好地查看发生了什么:如您所见,没有
rollback
制作,只有您的commit
. 您将 (-1) 插入dbo.t2
其中,并且该行永久存在。这是因为错误只是语句终止。第二条语句失败,因此没有插入任何行,但是 (-1) 的插入没有回滚,并且正如您在错误之后看到的那样,您
transaction
仍然是open。commit
提交 -1 和 table的插入是你的truncation
。现在第二个测试:取消注释
set xact_abort on
,这只会使statment terminating
错误成为batch aborting
,所有的statements
内部事务都将rolled back
是,一旦错误发生,执行将被中断。所以
t1
table 永远不会被截断, (-1) in 的插入t2
将是rolled back
.现在应该如何编写代码:
您的代码应始终设置 xact_abort 并且它应该有
try..catch
块。你应该
rollback
从catch
广告throw
中做error
David 的参考资料很棒,但是如果您正在寻找一个实际示例来说明您应该做什么,这里有一个适合您的模块。我没有精确地复制您的示例,因为没有它您可以看到问题。
我建立了一个示例表并在其中弹出一些数据。我故意尝试将 NULL 插入 dbo.Fact 表中,这是不允许的。
然后我将我的语句包装在一个 TRY/CATCH 块中。在 Catch 块中,我添加了一个回滚命令和一个 throw,因此原始错误将返回给客户端。如果你省略了 throw 则不会返回任何错误。
如果您想查看原始行为,则需要删除 TRY/CATCH 并在其中添加 BEGIN TRANSACTION 和 COMMIT。因为即使使用 try/catch SQL 也会正确回滚事务。