SQL Server 触发器循环
我想知道是否可以在两个表上添加一个触发器,将数据复制到另一个表。
例如:
我有两个用户表,users_V1 和 users_V2,当用户使用其中一个 V1 应用程序更新时,它也会激活一个触发器,在 users_V2 中更新它。
如果我想在V2表上添加相同的触发器,以便在V2中更新用户时更新V1中的数据,会不会陷入无限循环?有没有办法避免这种情况。
I would like to know if there is anyway I can add a trigger on two tables that will replicate the data to the other.
For example:
I have a two users tables, users_V1 and users_V2, When a user is updated with one of the V1 app, it activate a trigger updating it in users_V2 as well.
If I want to add the same trigger on the V2 table in order to update the data in V1 when a user is updated in V2, will it go into an infinite loop? Is there any way to avoid that.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我不建议在处理过程中显式禁用触发器 - 这可能会导致奇怪的副作用。
检测(和防止)触发器中的循环的最可靠方法是使用
CONTEXT_INFO()
。示例:
有关更详细的示例,请参阅此链接。
关于 SQL Server 2000 中
CONTEXT_INFO()
的注意事项:支持上下文信息,但显然不支持
CONTEXT_INFO
函数。你必须改用这个:I don't recommend explicitly disabling the trigger during processing - this can cause strange side-effects.
The most reliable way to detect (and prevent) cycles in a trigger is to use
CONTEXT_INFO()
.Example:
See this link for a more detailed example.
Note on
CONTEXT_INFO()
in SQL Server 2000:Context info is supported but apparently the
CONTEXT_INFO
function is not. You have to use this instead:使用 TRIGGER_NESTLEVEL() 限制触发器递归,或者 < /p>
Either use TRIGGER_NESTLEVEL() to restrict trigger recursion, or
check the target table whether an UPDATE is necessary at all:
我遇到了完全相同的问题。我尝试使用 CONTEXT_INFO() 但这是一个会话变量,因此它仅在第一次有效!然后下次在会话期间触发触发器时,这将不起作用。因此,我最终使用了一个变量,该变量在每个受影响的触发器中返回嵌套级别以退出。
示例:
注意:或者如果您想停止所有嵌套调用,请使用 @@NESTLEVEL>0
另请注意——本文中关于嵌套调用和递归调用似乎存在很多混淆。最初的海报指的是嵌套触发器,其中一个触发器将导致另一个触发器触发,这将导致第一个触发器再次触发,依此类推。这是嵌套的,但根据 SQL Server,不是递归的,因为触发器不直接调用/触发自身。递归并不是“一个触发器调用另一个触发器”。这是嵌套的,但不一定是递归的。您可以通过启用/禁用递归和嵌套来测试这里提到的一些设置:有关嵌套的博客文章
I had the exact same problem. I tried using CONTEXT_INFO() but that is a session variable and so it works only the first time! Then next time a trigger fires during the session, this won't work. So I ended up with using a variable that returns Nest Level in each of the affected triggers to exit.
Example:
Note: Or use @@NESTLEVEL>0 if you want to stop all nested calls
One other note -- There seems to be much confusion in this article about nested calls and recursive calls. The original poster was referring to a nested trigger where one trigger would cause another trigger to fire, which would cause the first trigger to fire again, and so on. This is Nested, but according to SQL Server, not recursive because the trigger is not calling/triggering itself directly. Recursion is NOT where "one trigger [is] calling another". That is nested, but not necessarily recursive. You can test this by enabling/disabling recursion and nesting with some settings mentioned here: blog post on nesting
对于这个特定的设计场景,我属于无触发阵营。话虽如此,由于我对您的应用程序的功能和原因了解有限,以下是我的总体分析:
在表上使用触发器的优点是能够对表上的所有操作执行操作。这就是您在这种情况下的主要好处。但这意味着您拥有可以直接访问该表的用户或对该表的多个访问点。我倾向于避免这种情况。触发器有其用武之地(我经常使用它们),但它是我最后使用的数据库设计工具之一,因为它们往往不太了解其上下文(通常是优势)以及何时在需要的地方使用了解不同的背景和总体用例,它们的好处就会被削弱。
如果两个应用版本都需要触发相同的操作,则它们都应该调用相同存储过程。存储过程可以确保完成所有适当的工作,并且当您的应用程序不再需要支持V1时,可以删除该部分存储过程。
在客户端代码中调用两个存储过程是一个坏主意,因为这是数据库可以轻松一致地提供的数据服务的抽象层,而无需您的应用程序担心它。
我更喜欢更多地控制底层表的接口 - 使用视图、UDF 或 SP。用户永远无法直接访问表。这里的另一点是,您可以呈现单个“用户”VIEW 或 UDF 合并适当的基础表,而用户甚至不知道 - 也许甚至不需要任何“同步”,因为新属性位于EAV 系统,如果您需要那种病态的灵活性或仍然可以连接的其他不同结构 - 例如 OUTER APPLY UDF 等。
I'm with the no triggers camp for this particular design scenario. Having said that, with the limited knowledge I have about what your app does and why it does it, here's my overall analysis:
Using a trigger on a table has an advantage of being able to act on all actions on the table. That's it, your main benefit in this case. But that would mean you have users with direct access to the table or multiple access points to the table. I tend to avoid that. Triggers have their place (I use them a lot), but it's one of the last database design tools I use because they tend to not know a lot about their context (generally, a strength) and when used in a place where they do need to know about different contexts and overall use cases, their benefits are weakened.
If both app versions need to trigger the same action, they should both call the same stored proc. The stored proc can ensure that all the appropriate work is done, and when your app no longer needs to support V1, then that part of the stored proc can be removed.
Calling two stored procs in your client code is a bad idea, because this is an abstraction layer of data services which the database can provide easily and consistently, without your application being worried about it.
I prefer to control the interface to the underlying tables more - with either views or UDFs or SPs. Users never get direct access to a table. Another point here is that you could present a single "users" VIEW or UDF coalescing the appropriate underlying tables without the user even knowing about - perhaps getting to the point where there is not even any "synchronization" necessary, since new attributes are in an EAV system if you need that kind of pathological flexibility or in some other different structure which can still be joined - say OUTER APPLY UDF etc.
您必须在触发器中创建某种环回检测。也许在将记录输入下一个表之前使用“如果存在”语句来查看该记录是否存在。听起来确实会按照当前设置的方式进入无限循环。
You're going to have to create some sort of loopback detection within your trigger. Perhaps using an "if exists" statement to see if the record exists before entering it into the next table. It does sound like it will go into an infinite loop the way it's currently set up.
避免像瘟疫这样的触发器......使用存储过程来添加用户。如果这需要一些设计更改,那么就进行更改。触发因素是邪恶。
Avoid triggers like the plague .... use a stored procedure to add the user. If this requires some design changes then make them. Triggers are the EVIL.
尝试类似的东西(我没有打扰创建触发器的东西,因为你显然已经知道如何编写该部分):
Try something like (I didn;t bother with thecreate trigger stuff as you clearly already know how to write that part):
触发器中的递归,即一个触发器调用另一个触发器,仅限于 32 层
在每个触发器中,只需检查您要插入的行是否已存在。
示例
Recursion in triggers, that is, one trigger calling another, is limited to 32 levels
In each trigger, just check if the row you wish to insert already exists.
Example