触发器被激活之后,会自动开启一个事务,可为什么下面的这个触发器工作之后却报错,请高手指点:
CREATE TABLE T_7_27_01
(
c1 nchar(2) primary key,
c2 int
)
gocreate TRIGGER TR_7_27_01
ON T_7_27_01
FOR INSERT
AS
DECLARE @n int
SELECT @n = c2 FROM INSERTED
if(@n not between 1 and 100)
BEGIN
rollback transaction
print 'error'
END
GO当往 T_7_27_01表中插入不符合业务规则的记录时,报如下错误:INSERT INTO T_7_27_01 VALUES('01',120)
====================================================error
消息 3609,级别 16,状态 1,第 1 行
事务在触发器中结束。批处理已中止。
CREATE TABLE T_7_27_01
(
c1 nchar(2) primary key,
c2 int
)
gocreate TRIGGER TR_7_27_01
ON T_7_27_01
FOR INSERT
AS
DECLARE @n int
SELECT @n = c2 FROM INSERTED
if(@n not between 1 and 100)
BEGIN
rollback transaction
print 'error'
END
GO当往 T_7_27_01表中插入不符合业务规则的记录时,报如下错误:INSERT INTO T_7_27_01 VALUES('01',120)
====================================================error
消息 3609,级别 16,状态 1,第 1 行
事务在触发器中结束。批处理已中止。
--如果只是为了防止c2这个列,插入非1到100的数字,可以加事前触发器,在添加之前就出发。这样:
CREATE TABLE T_7_27_01
(
c1 nchar(2) primary key,
c2 int
)
gocreate TRIGGER TR_7_27_01 ON T_7_27_01 instead of INSERT
AS
insert into T_7_27_01 select * from inserted where c2 between 1 and 100
GOINSERT INTO T_7_27_01 VALUES('01',120)
--如果修改时,也不允许c2列出现1到100之外的数字,可以这样:
CREATE TABLE T_7_27_01
(
c1 nchar(2) primary key,
c2 int
)
gocreate TRIGGER TR_7_27_01 ON T_7_27_01 instead of INSERT,update
AS
update T_7_27_01 set c2=c.c2 from T_7_27_01 a,inserted b,deleted c where a.c1=b.c1 and b.c1=c.c1 and b.c1 not between 1 and 100
insert into T_7_27_01 select * from inserted a where c2 between 1 and 100 and not exists(select 1 from deleted where c1=a.c1)
GOINSERT INTO T_7_27_01 VALUES('01',120) --插入失败INSERT INTO T_7_27_01 VALUES('02',10) --插入成功update T_7_27_01 set c2=140 where c1='02' --修改失败--查看结果
select * from T_7_27_01
在触发器中
当执行触发器时,触发器的操作好像有一个未完成的事务在起作用。不管激发触发器的语句在隐式事务中还是显式事务中,都会这样。当语句开始以自动提交模式执行时,如果遇到错误,可以通过隐式 BEGIN TRANSACTION 语句恢复该语句生成的所有修改。此隐式事务对批处理中的其他语句没有影响,因为当语句完成时,此事务要么提交,要么回滚。但是,当调用触发器时,此隐式事务仍然有效。执行触发器时,将开始隐式事务。如果触发器执行完后 @@TRANCOUNT 为 0,则会出现错误 3609 并终止批处理。由于这个原因,建议避免在触发器内部使用 ROLLBACK TRANSACTION(它将把 @@TRANCOUNT 重置为 0)以及 COMMIT TRANSACTION(它将把 @@TRANCOUNT 减少为 0)。在回滚后发出 BEGIN TRANSACTION 语句可以阻止错误的发生,但这样可能会导致应用程序逻辑出现问题。 了解在触发器中发出 BEGIN TRANSACTION 语句实际上是开始了一个嵌套事务这一点很重要。在这种情况下,执行 COMMIT TRANSACTION 语句将只应用到嵌套事务。因为回滚嵌套事务时将忽略嵌套的 BEGIN TRANSACTION 语句,所以触发器中执行的 ROLLBACK TRANSACTION 将回滚过去该触发器本身发出的所有 BEGIN TRANSACTION 语句。ROLLBACK 回滚到最外部事务并将 @@TRANCOUNT 设置为 0。在触发器使用 ROLLBACK TRANSACTION 时,请注意下列行为:当前事务中该时间点之前所做的所有数据修改都将回滚,包括触发器所做的修改。
触发器继续执行 ROLLBACK 语句之后的所有语句。如果这些语句中的任意语句修改数据,则不回滚这些修改。
触发器中的 ROLLBACK 关闭并释放所有在包含激发触发器的语句的批处理中声明和打开的游标。这包括在激发触发器的批处理所调用的存储过程中声明和打开的游标。在激发触发器的批处理之前的批处理中声明的游标只关闭。但是,STATIC 或 INSENSITIVE 游标在下列条件下不会关闭:
CURSOR_CLOSE_ON_COMMIT 设置为 OFF。
静态游标是同步游标或者完全填充的异步游标。
可以不使用 ROLLBACK TRANSACTION,而使用 SAVE TRANSACTION 语句在触发器中执行部分回滚。
当触发器被激活后,执行:
rollback transaction
print 'error'我的问题是:当事务回滚时,为什么会报错:
消息 3609,级别 16,状态 1,第 1 行
事务在触发器中结束。批处理已中止。==============================
我的理解是:触发器被激活之后,会自动开启一个事务,那么回滚事务时应该不会报错才对啊。
我也知道有INSTEAD OF触发器,并且用它来实现确实不会有错,那么用after触发器什么会出错呢?