在SQL Server中编写插入触发器的正确​​方法是什么?

时间:2016-02-12 12:11:52

标签: sql-server

我的问题有点理论化,因为我没有任何具体的工作实例。但我认为值得回答。

在SQL Server中编写插入触发器的正确​​方法是什么?

假设我创建了一个这样的触发器(或多或少伪代码)

CREATE TRIGGER ANOTHER_TRIGGER 
ON MY_TABLE
FOR INSERT AS 
    UPDATE MY_TABLE 
    SET A_COLUMN = something 
    WHERE ID IN (SELECT ID FROM INSERTED);

...etc

现在如果其他人在同一个表上创建另一个触发器并且该触发器会更改插入行上的某些列,该怎么办?像这样的东西

INSERTED

然后我的触发器(如果在另一个触发器之后被触发)对错误的数据进行操作,因为INSERTED数据与表中已经用另一个触发器更改的实际插入数据不同?

要点:

触发器A更新表T上的新插入行,触发器B然后对脏数据进行操作,因为触发器A的更新在触发器B操作的INSERTED伪表中不可见。但是如果触发器B直接在表上操作而不是在伪表INSERTED上操作,它将通过触发器A看到更新的数据。

这是真的吗?我应该始终使用表格本身而不是{{1}}表中的数据吗?

4 个答案:

答案 0 :(得分:4)

我通常建议不要使用多个触发器。只有两个,如果你愿意的话,你可以define what order希望它们能够运行。虽然你还有一些,但是你无法控制非第一个,非最后一个触发器的顺序运行

它也越来越难以推断插入过程中发生了什么。

我建议每个操作单个触发器,每个操作,以完成该操作应该发生的所有任务。如果您关注所产生的代码的大小,那通常表明您应该将该代码一起移出触发器 - 触发器应该快速而轻便。

相反,你应该开始考虑让触发器记录一个动作,然后使用例如服务代理或SQL Server作业,用于获取这些记录并执行其他处理。重要的是,它在自己的交易中执行此操作,而不是延迟原始INSERT

我还要警告你在示例1中显示的当前代码。不要使用游标并逐个插入行,而应考虑直接编写引用INSERT ... SELECT的{​​{1}}语句。并将所有新行插入另一个表中。

答案 1 :(得分:3)

在触发器中你应该完全避免的一件事是使用 CURSOR

触发器应该非常灵活,小巧,快速 - 并且光标不是任何东西!毕竟,它正在导致它触发的事务的上下文中执行。不要不必要地延迟完成该交易!

您还需要注意Inserted 包含多行并相应地编写触发器,但使用基于集合技术 - 不是游标和while循环 - 快速,快速地保持你的触发器。

不要在触发器中进行繁重,耗时的工作 - 只需更新几列,或进入另一个表 - 这很好 - 没有繁重的工作!< / strong>并且没有发送电子邮件等!

答案 2 :(得分:1)

我的SQL触发器幸福个人指南

  1. 触发器应该轻巧快速。昂贵的触发器使得 EVERYBODY 的数据库速度变慢(对于包括触发器作者在内的所有相关人员而言,并非偶然的不快乐)
  2. 请一个触发操作表组合。这在foo表上最多只有一个插入触发器。虽然表上多次操作的相同触发器不一定是坏的。
  3. 不要忘记inserteddeleted表可能包含多行甚至根本没有行。无论操作涉及多少行,一个快乐的触发器(更重要的是快乐的数据库用户和管理员)都会表现得很好。
  4. 不要 NOT 在触发器中使用游标。服务器端游标通常是滥用良好做法,尽管在极少数情况下使用它们是合理的。触发器从不其中之一。而是将一系列面向集合的DML语句改为类似于触发器的任何东西。
  5. 请记住,有两类触发器 - AFTER触发器和INSTEAD OF触发器。在编写触发器时请考虑这一点。
  6. 永远不要忽视触发器(AFTERINSTEAD OF)开始执行@@trancount一个大于触发它们的语句运行的上下文。
  7. 首选触发器的声明性引用完整性(DRI)作为保持数据库中数据一致的方法。一些应用完整性规则需要触发器但是多年来DRI已经走过了漫长的道路,像row_number()这样的功能使触发器变得不那么必要了。

答案 3 :(得分:0)

触发器是事务性的。如果您尝试按照您的描述进行循环更新,则应该导致死锁 - 第一次更新将阻止第二次更新完成。

虽然看了这段代码,但是你试图通过INSERTED伪表来进行插入操作 - 示例中没有任何内容需要这种行为。如果您只是直接从完整的INSERTED表中插入,您将获得明显的改进,并且还可以减少第二次触发器的点击。