假设我们有以下命令:
SET XACT_ABORT OFF;
SET IMPLICIT_TRANSACTIONS OFF
DECLARE @index int
SET @index = 4;
DECLARE @errorCount int
SET @errorCount = 0;
BEGIN TRANSACTION
WHILE @index > 0
BEGIN
SAVE TRANSACTION Foo;
BEGIN TRY
-- commands to execute...
INSERT INTO AppDb.dbo.Customers VALUES('Jalal', '1990-03-02');
-- make a problem
IF @index = 3
INSERT INTO AppDb.dbo.Customers VALUES('Jalal', '9999-99-99');
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION Foo; -- I want to keep track of previous logs but not works! :(
INSERT INTO AppDb.dbo.LogScripts VALUES(NULL, 'error', 'Customers', suser_name());
SET @errorCount = @errorCount + 1;
END CATCH
SET @index = @index - 1;
END
IF @errorCount > 0
ROLLBACK TRANSACTION
ELSE
COMMIT TRANSACTION
我想执行批处理,将所有错误保留在日志中,然后,如果没有发生错误,则提交所有更改。如何在Sql Server中实现它?
答案 0 :(得分:3)
事务与连接相关联,因此,所有写入都将在外部ROLLBACK TRANSACTION
上回滚(与嵌套保存点无关)。
您可以做的是将错误记录到内存结构中,如Table Variable,然后,在提交/回滚外部事务之后,您可以插入收集的日志。
为简洁起见,我简化了您的Logs
和Customers
表:
CREATE TABLE [dbo].[Logs](
[Description] [nvarchar](max) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[Customers](
[ID] [int] NOT NULL,
[Name] [nvarchar](50) NULL
);
GO
然后您可以跟踪表变量中的日志:
SET XACT_ABORT OFF;
SET IMPLICIT_TRANSACTIONS OFF
GO
DECLARE @index int;
SET @index = 4;
DECLARE @errorCount int
SET @errorCount = 0;
-- In memory storage to accumulate logs, outside of the transaction
DECLARE @TempLogs AS TABLE (Description NVARCHAR(MAX));
BEGIN TRANSACTION
WHILE @index > 0
BEGIN
-- SAVE TRANSACTION Foo; As per commentary below, savepoint is futile here
BEGIN TRY
-- commands to execute...
INSERT INTO Customers VALUES(1, 'Jalal');
-- make a problem
IF @index = 3
INSERT INTO Customers VALUES(NULL, 'Broken');
END TRY
BEGIN CATCH
-- ROLLBACK TRANSACTION Foo; -- Would roll back to the savepoint
INSERT INTO @TempLogs(Description)
VALUES ('Something bad happened on index ' + CAST(@index AS VARCHAR(50)));
SET @errorCount = @errorCount + 1;
END CATCH
SET @index = @index - 1;
END
IF @errorCount > 0
ROLLBACK TRANSACTION
ELSE
COMMIT TRANSACTION
-- Finally, do the actual insertion of logs, outside the boundaries of the transaction.
INSERT INTO dbo.Logs(Description)
SELECT Description FROM @TempLogs;
需要注意的一点是,这是处理数据的一种非常昂贵的方式(即尝试插入所有数据,然后在遇到任何问题时回滚批处理)。这里的另一种选择是在尝试插入任何数据之前验证所有数据(以及返回和报告错误)。
此外,在上面的示例中,Savepoint没有任何实际用途,因为如果检测到批次的任何错误,即使“成功”的客户插入也将最终回滚。
SqlFiddle here - 循环完成,尽管插入了3个客户,但ROLLBACK TRANSACTION
删除了所有成功插入的客户。但是,仍然会写入日志,因为表变量不受外部事务的影响。