这样说吧,你的应用涉及到间接递归触发器,那就需要在触发器代码中防止无限递归,有几个方法可以防止无限递归
1用update()函数判断修改的字段,这只能避免一部分
2查看修改前后的值,进行比较,真正的值作了修改才递交update
1用update()函数判断修改的字段,这只能避免一部分
2查看修改前后的值,进行比较,真正的值作了修改才递交update
解决方案 »
- 请教SQL高手:非固定时间段分类统计查询问题 (要求不用游标)
- 查询
- 大家目测一下这结果该是什么
- 大家好,我想通过JDBC访问一台安全性设置为Windows验证的SQL Server,请问如何写创建数据库连接的程序呀?
- 这句sql错在哪里? 错误提示为以下"NULL 请输入 goods_tiaoxingma 字段的文件存储类型 [char]: "
- 删除表中重复记录及更新相关表信息
- 请帮我看一下这是怎么转换的。不用存储过程可以运行
- vb程序员帮忙。CONST 常量名=&H80000000 的&H80000000是什么意思。
- 這條SQL語句怎麼寫?
- 请教高手一个简单问题。
- SQL语句随机查询数据?
- 百分求解CONTAINS问题
1. 使用 update(列名) 函数
对于"A表update后,取B表某列再次UPDATE A表"
如果仅更新A表的某些列才触发 UPDATE B, 并且 B 表再次 UPDATE A表不会包含A表触发UPDATE B的那些列, 则在A表的触发器中, 使用 IF UPDATE(列) 来确定是否应该UPDATE B即可
2. 使用 @@NESTLEVEL
该变量值确定嵌套层数
对于"A表update后,取B表某列再次UPDATE A表"
如果触发者不是一个存储过程, 则 UPDATE A 的A表触发器 @@NESTLEVEL = 1, 到UPDATE B时, B表触发器 @@NESTLEVEL = 2, B表触发器再 UPDATE A时, @@NESTLEVEL = 3
所以如果 @@NESTLEVEL >=3 时, 一般表示递归了(当然, 前提是 UPDATE A的触发器本身没有两层的递归, 即不能是存储过程再调用存储过程去 UPDATE A
使用 @@PROCID
该全局变量返回调用者的 object_id , 在A表触发器中使用它就可以知道触发者是谁, 如果是B表触发器, 则不处理就行了, 类似下面这样:IF OBJECT_ID(N'B表触发器名称') = @@PROCID
BEGIN
PRINT 'B表触发器, 不处理'
RETURN
END
-------------------------------------这是什么意思? 你是说不在 B 中递交 UPDATE A么? 如果是的话, 岂不是与你的需求冲突? 你的需求是要 UPDATE A 的嘛, 这个当然会触发A表触发器, 那也当然需要在A表触发器判断了
--建表
create table T1(
id int not null PRIMARY KEY,
value int
)
gocreate table T2(
id int not null PRIMARY KEY,
value int
)
go--触发器代码
Create Trigger tr_T1_Insert
on T1
for Insert
as
set nocount on
if exists ( --这个判断是防止无限递归的
select * from inserted i
where not exists (
select 1 from T2 where id=i.id
)
)
insert T2
select * from inserted i
where not exists (
select 1 from T2 where id=i.id
)print '1I' --报告触发器
goCreate Trigger tr_T2_Insert
on T2
for Insert
as
set nocount on
if exists ( --这个判断是防止无限递归的
select * from inserted i
where not exists (
select 1 from T1 where id=i.id
)
)
insert T1
select * from inserted i
where not exists (
select 1 from T1 where id=i.id
)
print '2I' --报告触发器
goCreate Trigger tr_T1_Update
on T1
for update
as
set nocount on
if update (value)
begin
if exists ( --这个判断是防止无限递归的
select 1
from T2,Inserted i,deleted d
where i.id=d.id
and i.id=T2.id
and i.value<>d.value
)
update T2
set value=i.Value
from T2,Inserted i,deleted d
where i.id=d.id
and i.id=T2.id
and i.value<>d.value
endprint '1U' --报告触发器
goCreate Trigger tr_T2_Update
on T2
for update
as
set nocount on
if update (value)
begin
if exists ( --这个判断是防止无限递归的
select 1
from T1,Inserted i,deleted d
where i.id=d.id
and i.id=T1.id
and i.value<>d.value
)
update T1
set value=i.Value
from T1,Inserted i,deleted d
where i.id=d.id
and i.id=T1.id
and i.value<>d.value
endprint '2U' --报告触发器
go
Create Trigger tr_T1_delete
on T1
for delete
as
set nocount on
if exists ( --这个判断是防止无限递归的
select 1 from T2,deleted d
where T2.id=d.Id
)
delete T2
from deleted d
where T2.id=d.Id
print '1D' --报告触发器
go
Create Trigger tr_T2_delete
on T2
for delete
as
set nocount on
if exists ( --这个判断是防止无限递归的
select 1 from T1,deleted d
where T1.id=d.Id
)
delete T1
from deleted d
where T1.id=d.Id
print '2D' --报告触发器
go--设置递归触发器
alter database ygback set RECURSIVE_TRIGGERS ON
go--测试1
insert T1 values(1,1)--结果1
2I
1I
select * from T2
id value
----------- -----------
1 1--测试2
insert T1 select 2,2 union all select 3,3
--结果2
2I
1I
select * from T2
id value
----------- -----------
1 1
2 2
3 3--测试3
update T1
set value=5
where id=1--结果3
1U
2U
1U
select * from T2
id value
----------- -----------
1 5
2 2
3 3--测试4
update T1
set value=value+1
where id>1
--结果4
1U
2U
1U
select * from T2
id value
----------- -----------
1 5
2 3
3 4
--测试5
delete T1
where id=1
--结果4
2D
1D
select * from T2
id value
----------- -----------
2 3
3 4
故只需要在A表的UPDATE触发器判断是否UPDATE(ID)即可嘛, 类似:IF UPDATE(ID)
BEGIN
.....如果更新则重新从表B中取回ID
END
ELSE
BEGIN
... 做正常的处理
END
HeadKey int
SerialNO VARCHAR(15)
表B:
ID INT (自动增长)
HeadKey int
要求:
A表插入时,同时插入B表,A表SerialNO将得到B表中ID的格式化字符。如果此时A表中SerialNO 被用户修改,则重新从表B中找回。
CREATE TRIGGER TR_A ON 表A
FOR INSERT
AS
INSERT 表B SELECT HeadKey FROM inserted
UPDATE A SET
SerialNO = B.ID
FROM 表A A, 表B B, inserted I
WHERE A.HeadKey = B.HeadKey
AND A.HeadKey = I.HeadKey
GOCREATE TRIGGER TR_A ON 表A
FOR UPDATE
AS
IF UPDATE(SerialNO)
UPDATE A SET
SerialNO = B.ID
FROM 表A A, 表B B, inserted I
WHERE A.HeadKey = B.HeadKey
AND A.HeadKey = I.HeadKey
GO
on T2
for update
as
set nocount on
if update (value)
begin
if exists ( --这个判断是防止无限递归的
select 1
from T1,Inserted i,deleted d
where i.id=d.id
and i.id=T1.id
and i.value<>d.value
)
update T1
set value=i.Value
from T1,Inserted i,deleted d
where i.id=d.id
and i.id=T1.id
and i.value<>d.value
end 可以解决的
FOR UPDATE
AS
IF UPDATE(SerialNO)
UPDATE A SET
SerialNO = B.ID
FROM 表A A, 表B B, inserted I
WHERE A.HeadKey = B.HeadKey
AND A.HeadKey = I.HeadKey
GO
这样不是无限递归了吗?
如果此时A表中SerialNO 被用户修改,则重新从表B中找回
---------------------------------------------------既然是这样, 感觉还不如如果有要胆敢更新 SerialNO, 则直接不让他的 UPDATE 成功(既然是违反政策方针政策, 姑息养奸没必要嘛)CREATE TRIGGER TR_A ON 表A
FOR UPDATE
AS
IF UPDATE(SerialNO)
BEGIN
RAISERROR(N'禁止更新 SerialNO, 你的操作被取消', 1)
ROLLBACK TRAN
END
GO
-----------------------------楼主提到的这个叫直接递归, 这个是数据库选项 RECURSIVE_TRIGGERS 控制的, 默认是关闭的, 难道楼主有将其打开么?
SerialNO = B.ID
FROM 表A A, 表B B, inserted I
WHERE A.HeadKey = B.HeadKey
AND A.HeadKey = I.HeadKey
不会触发表A的UPDATE吗?
CREATE TRIGGER TR_A ON 表A
FOR UPDATE
AS
IF UPDATE(SerialNO) AND @@PROCID <> OBJECT_ID(N'TR_A')
BEGIN
RAISERROR(N'禁止更新 SerialNO, 你的操作被取消', 1)
ROLLBACK TRAN
END
GO
A表触发器更新B, 表表触发器再更新A, 这种引发的叫间接递归, 是否允许由服务器的nested triggers 选项控制, 默认是打开的(即允许的)
表A:
HeadKey int
SerialNO VARCHAR(15)
表B:
ID INT (自动增长)
HeadKey int
要求:
A表插入时,同时插入B表,A表SerialNO将得到B表中ID的格式化字符。
如果此时A表中SerialNO 被用户修改,则重新从表B中找回。
---------------------------------------------------------------------
如下触发器,应该不会递归create trigger tr_A_Insert
on A
for insert
as
set nocount on
insert B (HeadKey) select HeadKey from insertedgo
create trigger tr_B_Insert
on B
for insert
as
set nocount on
if exists (
select 1 from A, inserted i
where A.HeadKey=i.HeadKey
and A.SerialNO<>格式化(i.ID)
)
update A
set SerialNO<>格式化(i.ID)
from A, inserted i
where A.HeadKey=i.HeadKey
and A.SerialNO<>格式化(i.ID)go