在 WHILE 循环中插入记录时出现奇怪的 TRY CATCH 行为
Strange TRY CATCH behavior that occurs when inserting records inside a WHILE LOOP
下面是我 运行 的代码(准备复制粘贴到 SQL 服务器)。我本质上是在尝试使用 WHILE LOOP 将记录从一个 table 一个接一个地插入到一个新的 table 中,并使用第三个 table 记录每次插入的结果。一旦我解决了这个问题,这段代码将被调整并用于几个存储过程。
代码如下:
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION
--Create dummy tables
DROP TABLE IF EXISTS #OldTable
DROP TABLE IF EXISTS #NewTable
DROP TABLE IF EXISTS #LoggingTable
CREATE TABLE #OldTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,OldValue varchar(64)
)
INSERT INTO #OldTable
VALUES
('1')
,('2')
,('3')
,('Four')
,('Five')
,('6')
,('Seven')
CREATE TABLE #NewTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,NewValue int
)
CREATE TABLE #LoggingTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,OldTableID int NULL
,NewTableID int NULL
,InsertStatus varchar(MAX)
)
--Begin insert loop
DECLARE @currentID int = NULL
DECLARE THIS_CURSOR CURSOR FAST_FORWARD FOR
SELECT ID FROM #OldTable ORDER BY ID
OPEN THIS_CURSOR
FETCH NEXT FROM THIS_CURSOR INTO @currentID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
--Perform insert
INSERT INTO #NewTable
OUTPUT @currentID, INSERTED.ID, 'Insert successful' INTO #LoggingTable (OldTableID, NewTableID, InsertStatus)
SELECT CAST(OldValue AS int) FROM #OldTable WHERE ID = @currentID
FETCH NEXT FROM THIS_CURSOR INTO @currentID
END TRY
BEGIN CATCH
INSERT INTO #LoggingTable (OldTableID, NewTableID, InsertStatus) VALUES (@currentID, NULL, 'Error occurred during insert operation')
FETCH NEXT FROM THIS_CURSOR INTO @currentID
END CATCH
END
CLOSE THIS_CURSOR
DEALLOCATE THIS_CURSOR
SELECT * FROM #OldTable
SELECT * FROM #NewTable
SELECT * FROM #LoggingTable
COMMIT TRANSACTION
END TRY
BEGIN CATCH
PRINT 'An error has occurred';
-- Test if the transaction is uncommittable.
IF XACT_STATE() = -1
BEGIN
PRINT 'The transaction is in an uncommittable state. Rolling back transaction.';
ROLLBACK TRANSACTION;
END;
-- Test if the transaction is committable.
IF XACT_STATE() = 1
BEGIN
PRINT 'The transaction is committable. Committing transaction.';
COMMIT TRANSACTION;
END;
THROW;
END CATCH
这是我收到的错误消息:
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
违规行似乎是这一行:
INSERT INTO #LoggingTable (OldTableID, NewTableID, InsertStatus) VALUES (@currentID, NULL, 'Error occurred during insert operation')
但是,如果我注释掉这一行(我仍然需要它!),它只会移动到代码中的另一行,这通常意味着还有其他事情正在发生。我已经在网上彻底搜索过了,我仍然不确定这个错误的原因是什么。
关于此错误最奇怪的部分是,如果您删除外部 TRY CATCH
和 TRANSACTION
,脚本将完全按预期运行,没有任何问题;但是,恕我直言,这不是一个可行的解决方案,因为一旦包含在存储过程中就需要适当的错误捕获和事务处理。
我向上移动了 "COMMIT TRANSACTION" 几行并且它工作正常(抛出异常是因为它试图在回滚事务之前写入 catch 块中的 table):
SET NOCOUNT ON;
BEGIN TRY
SET XACT_ABORT ON;
BEGIN TRANSACTION;
--Create dummy tables
DROP TABLE IF EXISTS #OldTable;
DROP TABLE IF EXISTS #NewTable;
DROP TABLE IF EXISTS #LoggingTable;
CREATE TABLE #OldTable
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
OldValue VARCHAR(64)
);
INSERT INTO #OldTable
VALUES
('1'),
('2'),
('3'),
('Four'),
('Five'),
('6'),
('Seven');
CREATE TABLE #NewTable
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
NewValue INT
);
CREATE TABLE #LoggingTable
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
OldTableID INT NULL,
NewTableID INT NULL,
InsertStatus VARCHAR(MAX)
);
--Begin insert loop
DECLARE @currentID INT = NULL;
DECLARE THIS_CURSOR CURSOR FAST_FORWARD FOR
SELECT ID
FROM #OldTable
ORDER BY ID;
OPEN THIS_CURSOR;
FETCH NEXT FROM THIS_CURSOR
INTO @currentID;
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
--Perform insert
INSERT INTO #NewTable
OUTPUT @currentID,
INSERTED.ID,
'Insert successful'
INTO #LoggingTable
(
OldTableID,
NewTableID,
InsertStatus
)
SELECT CAST(OldValue AS INT)
FROM #OldTable
WHERE ID = @currentID;
FETCH NEXT FROM THIS_CURSOR
INTO @currentID;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
INSERT INTO #LoggingTable
(
OldTableID,
NewTableID,
InsertStatus
)
VALUES
(@currentID, NULL, 'Error occurred during insert operation');
FETCH NEXT FROM THIS_CURSOR
INTO @currentID;
END CATCH;
END;
CLOSE THIS_CURSOR;
DEALLOCATE THIS_CURSOR;
SELECT *
FROM #OldTable;
SELECT *
FROM #NewTable;
SELECT *
FROM #LoggingTable;
SET XACT_ABORT OFF;
END TRY
BEGIN CATCH
PRINT 'An error has occurred';
-- Test if the transaction is uncommittable.
IF XACT_STATE() = -1
BEGIN
PRINT 'The transaction is in an uncommittable state. Rolling back transaction.';
ROLLBACK TRANSACTION;
END;
-- Test if the transaction is committable.
IF XACT_STATE() = 1
BEGIN
PRINT 'The transaction is committable. Committing transaction.';
COMMIT TRANSACTION;
END;
THROW;
END CATCH;
老实说,我认为这里的交易只是让事情变得比他们需要的更复杂。每个 DML 语句周围都有一个隐式事务。如果插入失败,游标的主体只不过是带有 catch 块的插入语句。我删除了一堆处理显式事务的无关代码,您的示例完美运行。
--Create dummy tables
DROP TABLE IF EXISTS #OldTable
DROP TABLE IF EXISTS #NewTable
DROP TABLE IF EXISTS #LoggingTable
CREATE TABLE #OldTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,OldValue varchar(64)
)
INSERT INTO #OldTable
VALUES
('1')
,('2')
,('3')
,('Four')
,('Five')
,('6')
,('Seven')
CREATE TABLE #NewTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,NewValue int
)
CREATE TABLE #LoggingTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,OldTableID int NULL
,NewTableID int NULL
,InsertStatus varchar(MAX)
)
--Begin insert loop
DECLARE @currentID int = NULL
DECLARE THIS_CURSOR CURSOR FAST_FORWARD FOR
SELECT ID FROM #OldTable ORDER BY ID
OPEN THIS_CURSOR
FETCH NEXT FROM THIS_CURSOR INTO @currentID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
--Perform insert
INSERT INTO #NewTable
OUTPUT @currentID, INSERTED.ID, 'Insert successful' INTO #LoggingTable (OldTableID, NewTableID, InsertStatus)
SELECT CAST(OldValue AS int) FROM #OldTable WHERE ID = @currentID
FETCH NEXT FROM THIS_CURSOR INTO @currentID
END TRY
BEGIN CATCH
INSERT INTO #LoggingTable (OldTableID, NewTableID, InsertStatus) VALUES (@currentID, NULL, 'Error occurred during insert operation')
FETCH NEXT FROM THIS_CURSOR INTO @currentID
END CATCH
END
close THIS_CURSOR
deallocate THIS_CURSOR
SELECT * FROM #OldTable
SELECT * FROM #NewTable
SELECT * FROM #LoggingTable
下面是我 运行 的代码(准备复制粘贴到 SQL 服务器)。我本质上是在尝试使用 WHILE LOOP 将记录从一个 table 一个接一个地插入到一个新的 table 中,并使用第三个 table 记录每次插入的结果。一旦我解决了这个问题,这段代码将被调整并用于几个存储过程。
代码如下:
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION
--Create dummy tables
DROP TABLE IF EXISTS #OldTable
DROP TABLE IF EXISTS #NewTable
DROP TABLE IF EXISTS #LoggingTable
CREATE TABLE #OldTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,OldValue varchar(64)
)
INSERT INTO #OldTable
VALUES
('1')
,('2')
,('3')
,('Four')
,('Five')
,('6')
,('Seven')
CREATE TABLE #NewTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,NewValue int
)
CREATE TABLE #LoggingTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,OldTableID int NULL
,NewTableID int NULL
,InsertStatus varchar(MAX)
)
--Begin insert loop
DECLARE @currentID int = NULL
DECLARE THIS_CURSOR CURSOR FAST_FORWARD FOR
SELECT ID FROM #OldTable ORDER BY ID
OPEN THIS_CURSOR
FETCH NEXT FROM THIS_CURSOR INTO @currentID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
--Perform insert
INSERT INTO #NewTable
OUTPUT @currentID, INSERTED.ID, 'Insert successful' INTO #LoggingTable (OldTableID, NewTableID, InsertStatus)
SELECT CAST(OldValue AS int) FROM #OldTable WHERE ID = @currentID
FETCH NEXT FROM THIS_CURSOR INTO @currentID
END TRY
BEGIN CATCH
INSERT INTO #LoggingTable (OldTableID, NewTableID, InsertStatus) VALUES (@currentID, NULL, 'Error occurred during insert operation')
FETCH NEXT FROM THIS_CURSOR INTO @currentID
END CATCH
END
CLOSE THIS_CURSOR
DEALLOCATE THIS_CURSOR
SELECT * FROM #OldTable
SELECT * FROM #NewTable
SELECT * FROM #LoggingTable
COMMIT TRANSACTION
END TRY
BEGIN CATCH
PRINT 'An error has occurred';
-- Test if the transaction is uncommittable.
IF XACT_STATE() = -1
BEGIN
PRINT 'The transaction is in an uncommittable state. Rolling back transaction.';
ROLLBACK TRANSACTION;
END;
-- Test if the transaction is committable.
IF XACT_STATE() = 1
BEGIN
PRINT 'The transaction is committable. Committing transaction.';
COMMIT TRANSACTION;
END;
THROW;
END CATCH
这是我收到的错误消息:
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
违规行似乎是这一行:
INSERT INTO #LoggingTable (OldTableID, NewTableID, InsertStatus) VALUES (@currentID, NULL, 'Error occurred during insert operation')
但是,如果我注释掉这一行(我仍然需要它!),它只会移动到代码中的另一行,这通常意味着还有其他事情正在发生。我已经在网上彻底搜索过了,我仍然不确定这个错误的原因是什么。
关于此错误最奇怪的部分是,如果您删除外部 TRY CATCH
和 TRANSACTION
,脚本将完全按预期运行,没有任何问题;但是,恕我直言,这不是一个可行的解决方案,因为一旦包含在存储过程中就需要适当的错误捕获和事务处理。
我向上移动了 "COMMIT TRANSACTION" 几行并且它工作正常(抛出异常是因为它试图在回滚事务之前写入 catch 块中的 table):
SET NOCOUNT ON;
BEGIN TRY
SET XACT_ABORT ON;
BEGIN TRANSACTION;
--Create dummy tables
DROP TABLE IF EXISTS #OldTable;
DROP TABLE IF EXISTS #NewTable;
DROP TABLE IF EXISTS #LoggingTable;
CREATE TABLE #OldTable
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
OldValue VARCHAR(64)
);
INSERT INTO #OldTable
VALUES
('1'),
('2'),
('3'),
('Four'),
('Five'),
('6'),
('Seven');
CREATE TABLE #NewTable
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
NewValue INT
);
CREATE TABLE #LoggingTable
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
OldTableID INT NULL,
NewTableID INT NULL,
InsertStatus VARCHAR(MAX)
);
--Begin insert loop
DECLARE @currentID INT = NULL;
DECLARE THIS_CURSOR CURSOR FAST_FORWARD FOR
SELECT ID
FROM #OldTable
ORDER BY ID;
OPEN THIS_CURSOR;
FETCH NEXT FROM THIS_CURSOR
INTO @currentID;
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
--Perform insert
INSERT INTO #NewTable
OUTPUT @currentID,
INSERTED.ID,
'Insert successful'
INTO #LoggingTable
(
OldTableID,
NewTableID,
InsertStatus
)
SELECT CAST(OldValue AS INT)
FROM #OldTable
WHERE ID = @currentID;
FETCH NEXT FROM THIS_CURSOR
INTO @currentID;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
INSERT INTO #LoggingTable
(
OldTableID,
NewTableID,
InsertStatus
)
VALUES
(@currentID, NULL, 'Error occurred during insert operation');
FETCH NEXT FROM THIS_CURSOR
INTO @currentID;
END CATCH;
END;
CLOSE THIS_CURSOR;
DEALLOCATE THIS_CURSOR;
SELECT *
FROM #OldTable;
SELECT *
FROM #NewTable;
SELECT *
FROM #LoggingTable;
SET XACT_ABORT OFF;
END TRY
BEGIN CATCH
PRINT 'An error has occurred';
-- Test if the transaction is uncommittable.
IF XACT_STATE() = -1
BEGIN
PRINT 'The transaction is in an uncommittable state. Rolling back transaction.';
ROLLBACK TRANSACTION;
END;
-- Test if the transaction is committable.
IF XACT_STATE() = 1
BEGIN
PRINT 'The transaction is committable. Committing transaction.';
COMMIT TRANSACTION;
END;
THROW;
END CATCH;
老实说,我认为这里的交易只是让事情变得比他们需要的更复杂。每个 DML 语句周围都有一个隐式事务。如果插入失败,游标的主体只不过是带有 catch 块的插入语句。我删除了一堆处理显式事务的无关代码,您的示例完美运行。
--Create dummy tables
DROP TABLE IF EXISTS #OldTable
DROP TABLE IF EXISTS #NewTable
DROP TABLE IF EXISTS #LoggingTable
CREATE TABLE #OldTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,OldValue varchar(64)
)
INSERT INTO #OldTable
VALUES
('1')
,('2')
,('3')
,('Four')
,('Five')
,('6')
,('Seven')
CREATE TABLE #NewTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,NewValue int
)
CREATE TABLE #LoggingTable (
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY
,OldTableID int NULL
,NewTableID int NULL
,InsertStatus varchar(MAX)
)
--Begin insert loop
DECLARE @currentID int = NULL
DECLARE THIS_CURSOR CURSOR FAST_FORWARD FOR
SELECT ID FROM #OldTable ORDER BY ID
OPEN THIS_CURSOR
FETCH NEXT FROM THIS_CURSOR INTO @currentID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
--Perform insert
INSERT INTO #NewTable
OUTPUT @currentID, INSERTED.ID, 'Insert successful' INTO #LoggingTable (OldTableID, NewTableID, InsertStatus)
SELECT CAST(OldValue AS int) FROM #OldTable WHERE ID = @currentID
FETCH NEXT FROM THIS_CURSOR INTO @currentID
END TRY
BEGIN CATCH
INSERT INTO #LoggingTable (OldTableID, NewTableID, InsertStatus) VALUES (@currentID, NULL, 'Error occurred during insert operation')
FETCH NEXT FROM THIS_CURSOR INTO @currentID
END CATCH
END
close THIS_CURSOR
deallocate THIS_CURSOR
SELECT * FROM #OldTable
SELECT * FROM #NewTable
SELECT * FROM #LoggingTable