我是SQL Server错误处理的新手,我的英语不太清楚,所以我提前为任何误解道歉。
问题是:我在表中插入了多条记录。该表有一个AFTER INSERT
触发器,它使用游标在FETCH WHILE循环中逐个处理记录。如果发生错误,一切都会回滚。因此,如果插入的记录中只有一个错误的字段,我就丢失了所有字段。插件也回滚,所以我找不到错误的记录。所以我需要处理游标内部的错误,只回滚错误的记录。
我用3个表创建了一个测试数据库:
TA
VarSmallint smallint
VarTinyint tinyint
String varchar(20)
TB
ID int (PK, identity)
Timestamp datetime (default: getdate())
VarSmallint smallint
VarTinyint tinyint
String varchar(20)
的tC
ID int PK
Timestamp datetime
VarTinyint1 tinyint
VarTinyint2 tinyint
String varchar(10)
tA
包含3条记录,其中1条记录错误。我将此内容插入tB
。
tB
具有触发器,并将记录插入tC
ony。
tC
只有tinyint变量,因此插入大于255的值可能会出现问题。这是测试错误发生的地方!
我的触发器是:
ALTER TRIGGER [dbo].[trg_tB]
ON [dbo].[tB]
AFTER INSERT
AS
BEGIN
IF @@rowcount = 0
RETURN;
SET NOCOUNT ON;
DECLARE
@ID AS int,
@Timestamp AS datetime,
@VarSmallint AS smallint,
@VarTinyint AS tinyint,
@String AS varchar(20),
DECLARE curNyers CURSOR DYNAMIC
FOR
SELECT
[ID], [Timestamp], [VarSmallint], [VarTinyint], [String]
FROM INSERTED
ORDER BY [ID]
OPEN curNyers
FETCH NEXT FROM curNyers INTO @ID, @Timestamp, @VarSmallint, @VarTinyint, @String
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
BEGIN TRAN
INSERT INTO [dbo].[tC]([ID], [Timestamp], [VarTinyint1], [VarTinyint2], [String])
VALUES (@ID, @Timestamp, @VarSmallint, @VarTinyint, @String)
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
INSERT INTO [dbo].[tErrorLog]([ErrorTime], [UserName], [ErrorNumber],
[ErrorSeverity], [ErrorState],
[ErrorProcedure], [ErrorLine],
[ErrorMessage], [RecordID])
VALUES (SYSDATETIME(), SUSER_NAME(), ERROR_NUMBER(),
ERROR_SEVERITY(), ERROR_STATE(),
ERROR_PROCEDURE(), ERROR_LINE(),
ERROR_MESSAGE(), @ID)
END CATCH
FETCH NEXT FROM curNyers INTO @ID, @Timestamp, @VarSmallint, @VarTinyint, @String
END
CLOSE curNyers
DEALLOCATE curNyers
END
如果我插入2条带有1个错误的好记录,那么一切都在回滚,我收到了错误:
Msg 3609,Level 16,State 1,Line 1
交易在触发器中结束。批次已中止。
请帮帮我!如何修改此触发器才能正常工作?
如果我插入了错误的记录,我需要:
谢谢!
答案 0 :(得分:1)
您的触发器中有两个主要灾难:
不要在触发器内使用光标 - 这太可怕了!当给定的操作发生时触发器触发 - 你几乎无法控制它们发射的次数和次数。因此,为了不过多地损害您的系统性能,触发器应该非常小,快速,灵活 - 执行不在触发器中进行任何繁重的处理和大量处理。光标是除了之外的任何东西都是敏捷而快速的 - 它是资源占用,处理器生猪,内存泄漏的怪物 - 避免那些随时可以,而且绝对是在触发器内! (并且你不需要它们,99%的情况,无论如何)
您可以将整个逻辑重写为一个快速,基于集合的语句:
ALTER TRIGGER [dbo].[trg_tB]
ON [dbo].[tB]
AFTER INSERT
AS
BEGIN
INSERT INTO [dbo].[tC]([ID], [Timestamp], [VarTinyint1], [VarTinyint2], [String])
SELECT
[ID], [Timestamp], [VarSmallint], [VarTinyint], [String]
FROM
INSERTED
END
切勿在触发器内调用COMMIT TRAN
。触发器在语句的上下文和事务中执行,导致它触发 - 如果一切正常,只需让触发器完成,然后事务就会正常提交。如果您需要中止,请致电ROLLBACK
。但永远不会在触发器中间调用COMMIT TRAN
。只是不要......
答案 1 :(得分:0)
我删除了TRIGGER
,并将代码中的代码复制粘贴到STORED PROCEDURE
中。
然后我向tB添加了一行Status
,并将默认值设置为0.
1是"记录处理OK",2是"记录处理故障"。
我用WHERE Status = 0
填充光标。
在TRY
部分,我将状态更新为1,在CATCH
部分UPDATE
将其更新为2.
我没有工作,因此我使用带有SQLCMD
命令的批处理文件从Windows调度程序运行SP。
现在处理效果很好,而且它第一次运作良好。谢谢你的帮助!