我想知道无论如何我都可以在两个表上添加触发器,将数据复制到另一个表。
例如:
我有两个用户表,users_V1和users_V2,当用一个V1应用程序更新用户时,它会激活一个触发器,在users_V2中更新它。
如果我想在V2表上添加相同的触发器,以便在V2中更新用户时更新V1中的数据,它是否会进入无限循环?有没有办法避免这种情况。
答案 0 :(得分:8)
我不建议在处理过程中明确禁用触发器 - 这可能会导致奇怪的副作用。
检测(和阻止)触发器中循环的最可靠方法是使用CONTEXT_INFO()
。
示例:
CREATE TRIGGER tr_Table1_Update
ON Table1
FOR UPDATE AS
DECLARE @ctx VARBINARY(128)
SELECT @ctx = CONTEXT_INFO()
IF @ctx = 0xFF
RETURN
SET @ctx = 0xFF
-- Trigger logic goes here
有关更详细的示例,请参阅this link。
关于SQL Server 2000中CONTEXT_INFO()
的注释:
支持上下文信息,但显然CONTEXT_INFO
函数不支持。你必须改为使用它:
SELECT @ctx = context_info
FROM master.dbo.sysprocesses
WHERE spid = @@SPID
答案 1 :(得分:6)
使用TRIGGER_NESTLEVEL()限制触发器递归,或者
检查目标表是否完全需要UPDATE:
IF (SELECT COUNT(1)
FROM users_V1
INNER JOIN inserted ON users_V1.ID = inserted.ID
WHERE users_V1.field1 <> inserted.field1
OR users_V1.field2 <> inserted.field2) > 0 BEGIN
UPDATE users_V1 SET ...
答案 2 :(得分:3)
我遇到了完全相同的问题。我尝试使用CONTEXT_INFO(),但这是一个会话变量,所以它只在第一次工作!然后,下次在会话期间触发触发器时,这将不起作用。所以我最终使用了一个变量,该变量在每个受影响的触发器中返回Nest Level以退出。
CREATE TRIGGER tr_Table1_Update
ON Table1
FOR UPDATE AS
BEGIN
--Prevents Second Nested Call
IF @@NESTLEVEL>1 RETURN
--Trigger logic goes here
END
注意:如果要停止所有嵌套调用,请使用@@ NESTLEVEL&gt; 0
另一个注意事项 - 本文中有关嵌套调用和递归调用的混淆似乎很多。原始海报是指嵌套触发器,其中一个触发器会导致另一个触发器触发,这将导致第一个触发器再次触发,依此类推。这是嵌套的,但根据SQL Server,不是递归的,因为触发器没有直接调用/触发它自己。递归不是“一个触发器正在调用另一个触发器”的地方。这是嵌套的,但不一定是递归的。您可以通过启用/禁用递归并使用此处提到的某些设置进行嵌套来测试此问题:blog post on nesting
答案 3 :(得分:1)
我正处于这个特定设计场景的无触发阵营。话虽如此,由于我对你的应用程序的作用以及为什么会这样做有了有限的知识,这是我的整体分析:
在表上使用触发器的优点是能够对表上的所有操作执行操作。就是这样,在这种情况下你的主要好处。但这意味着您可以让用户直接访问该表或访问该表的多个访问点。我倾向于避免这种情况。触发器有它们的位置(我经常使用它们),但它是我使用的最后一个数据库设计工具之一,因为它们往往不太了解它们的上下文(通常是强度),并且在它们确实需要的地方使用时要了解不同的背景和整体用例,他们的好处就会被削弱。
如果两个应用版本都需要触发相同的操作,则应同时调用相同存储过程。存储过程可以确保完成所有适当的工作,当您的应用程序不再需要支持V1时,可以删除存储过程的那部分。
在客户端代码中调用两个存储过程是一个坏主意,因为这是数据服务的抽象层,数据库可以轻松一致地提供,而不会让您的应用程序担心它。
我更喜欢使用视图或UDF或SP来控制基础表的接口。用户永远不会直接访问表。这里的另一点是,您可以呈现单个“用户”VIEW或UDF合并相应的基础表而用户甚至不知道 - 可能达到甚至不需要任何“同步”的点,因为新属性在EAV系统,如果你需要那种病态灵活性或其他一些可以加入的结构 - 比如OUTER APPLY UDF等。
答案 4 :(得分:0)
您将不得不在触发器中创建某种环回检测。也许使用“if exists”语句在将记录输入下一个表之前查看该记录是否存在。听起来它听起来像是当前设置的无限循环。
答案 5 :(得分:0)
触发器中的递归,即一个触发器调用另一个触发器,仅限于32 levels
在每个触发器中,只需检查您要插入的行是否已存在。
示例强>
CREATE TRIGGER Table1_Synchronize_Update ON [Table1] FOR UPDATE AS
BEGIN
UPDATE Table2
SET LastName = i.LastName
, FirstName = i.FirstName
, ... -- Every relevant field that needs to stay in sync
FROM Table2 t2
INNER JOIN Inserted i ON i.UserID = t2.UserID
WHERE i.LastName <> t2.LastName
OR i.FirstName <> t2.FirstName
OR ... -- Every relevant field that needs to stay in sync
END
CREATE TRIGGER Table1_Synchronize_Insert ON [Table1] FOR INSERT AS
BEGIN
INSERT INTO Table2
SELECT i.*
FROM Inserted i
LEFT OUTER JOIN Table2 t2 ON t2.UserID = i.UserID
WHERE t2.UserID IS NULL
END
CREATE TRIGGER Table2_Synchronize_Update ON [Table2] FOR UPDATE AS
BEGIN
UPDATE Table1
SET LastName = i.LastName
, FirstName = i.FirstName
, ... -- Every relevant field that needs to stay in sync
FROM Table1 t1
INNER JOIN Inserted i ON i.UserID = t1.UserID
WHERE i.LastName <> t1.LastName
OR i.FirstName <> t1.FirstName
OR ... -- Every relevant field that needs to stay in sync
END
CREATE TRIGGER Table2_Synchronize_Insert ON [Table2] FOR INSERT AS
BEGIN
INSERT INTO Table1
SELECT i.*
FROM Inserted i
LEFT OUTER JOIN Table1 t1 ON t1.UserID = i.UserID
WHERE t1.UserID IS NULL
END
答案 6 :(得分:0)
避免像瘟疫这样的触发器....使用存储过程添加用户。如果这需要一些设计更改,那么制作它们。触发器是EVIL。
答案 7 :(得分:0)
尝试类似的东西(我没有使用创建触发器的东西,因为你显然已经知道如何编写该部分):
update t
set field1 = i.field1
field2 = i.field2
from inserted i
join table1 t on i.id = t.id
where field1 <> i.field1 OR field2 <> i.field2