数据库触发器

时间:2008-08-18 00:01:19

标签: sql-server database-design triggers

过去我从来不喜欢在数据库表上使用触发器。对我来说,他们总是表现出一些将在数据库方面发生的“魔力”,远离我对应用程序代码的控制。我还想限制数据库必须完成的工作量,因为它通常是一个共享资源,我总是认为触发器在高负载情况下会变得昂贵。

那就是说,我发现了几个使用触发器的实例(至少在我看来它们是有意义的)。最近,我发现自己有时可能需要“绕过”触发器。我不得不寻找方法来做到这一点,我仍然感到内疚,我仍然认为更好的数据库设计可以减少对这种绕过的需求。不幸的是,这个数据库被多个应用程序使用,其中一些应用程序由一个非常不合作的开发团队维护,他们会对模式更改感到尖叫,所以我被卡住了。

关于触发器的一般理由是什么?爱他们?讨厌他们?认为它们在某些情况下有用吗? 是否认为需要绕过触发器意味着你“做错了”?

12 个答案:

答案 0 :(得分:12)

触发器通常使用不当,引入错误,因此应该避免。永远不要设计触发器来执行跨越表中行的完整性约束检查(例如“dept的平均工资不能超过X)。

Tom Kyte,Oracle副总裁表示他更喜欢remove triggers as a feature of the Oracle数据库,因为他们经常在bug中扮演角色。他知道这只是一个梦想,并且触发器会留在这里,但如果他可以从Oracle中删除触发器,他会(以及WHEN OTHERS子句和自治事务)。

  

可以正确使用触发器吗?绝对

     

问题是 - 它们没有正确使用   很多情况下我愿意给予   得到任何感知的好处   摆脱由此引起的滥用(和错误)   他们。 - 汤姆·基特

答案 1 :(得分:8)

将数据库视为一个伟大的大对象 - 在每次调用之后,它应该处于逻辑上一致的状态。

数据库通过表公开自己,并且可以使用触发器保持表和行的一致性。保持它们一致的另一种方法是禁止直接访问表,只允许它通过存储过程和视图。

触发器的缺点是任何动作都可以调用它们;这也是一种力量 - 没有人会因无能而搞砸系统的完整性。

作为一个对应点,仅允许通过存储过程和视图访问数据库仍允许后门访问权限。具有足够权限的用户被信任不会破坏数据库完整性,所有其他用户都使用存储过程。

关于减少工作量:当数据库不必处理外部世界时,它们的效率非常高;你甚至会惊讶于甚至过程切换会伤害性能。这是存储过程的另一个好处:而不是十几次调用数据库(以及所有相关的往返),只有一个。

  

在单个存储过程中收集内容很好,但是当出现问题时会发生什么?假设您有5个步骤,第一步失败,其他步骤会发生什么?你需要在那里添加一大堆逻辑来满足这种情况。一旦开始这样做,就会失去该场景中存储过程的好处。

业务逻辑必须走到某个地方,并且在数据库的设计中嵌入了许多隐含的域规则 - 关系,约束等是试图通过说,例如,用户只能拥有来编纂业务规则一个密码。鉴于您已经开始通过这些关系等将业务规则推送到数据库服务器上,您在哪里绘制线?数据库何时放弃对数据完整性的责任,并开始信任调用应用程序和数据库用户以使其正确无误?嵌入这些规则的存储过程可以将很多政治权力推到DBA手中。它归结为你的n层架构中将存在多少层;如果有演示文稿,业务和数据层,业务和数据之间的分离在哪里?业务层添加了什么增值功能?您是否将数据库服务器上的业务层作为存储过程运行?

是的,我认为必须绕过触发器意味着你“做错了”;在这种情况下,触发器不适合你。

enter image description here

答案 2 :(得分:2)

“永远不要设计触发器来执行跨越表中行的完整性约束检查” - 我不能同意。问题是标记为'SQL Server',SQL Server中的CHECK约束'子句不能包含子查询;更糟糕的是,实现似乎有一个'硬编码'的假设,即CHECK只涉及一行,所以使用函数是不可靠的。因此,如果我需要一个合法地涉及多行的约束 - 这里的一个好例子是经典“有效时间”时间表中的有序主键,我需要防止同一实体的重叠周期 - 如何我没有触发器那样做?请记住,这是一个主键,可以确保我拥有数据完整性,因此在DBMS以外的任何地方强制执行它都是不可能的。在CHECK约束得到子查询之前,我没有看到使用触发器替代某些类型的完整性约束的替代方法。

答案 3 :(得分:2)

我在网络上工作并在c#中赢取应用程序,并且我热情地 HATE 触发器。我从未遇到过这样的情况:我可以证明使用触发器将该逻辑移动到应用程序的业务层并在那里复制触发器逻辑。

我不做任何DTS类型的工作或类似的事情,所以可能有一些用例在那里使用触发器,但如果我的任何团队中的任何人说他们可能想要使用触发器他们最好准备他们的论点很好,因为我拒绝支持并将触发器添加到我正在处理的任何数据库中。

我不喜欢触发器的一些原因:

  • 他们将逻辑移入数据库。一旦你开始这样做,你就会要求一个痛苦的世界,因为你失去了调试,你的编译时间安全,你的逻辑流程。这一切都很艰难。
  • 任何人都不容易看到他们实施的逻辑。
  • 并非所有数据库引擎都支持触发器,因此您的解决方案会在数据库引擎上创建依赖关系

我确信我能想到更多的理由,但仅凭这些理由我就不会使用触发器了。

答案 4 :(得分:2)

触发器非常有用。他们也可能非常危险。我认为它们适用于房屋清洁任务,如填充审计数据(由创建日期,修改日期等),并且在某些数据库中可用于参照完整性。

但我并不是将大量业务逻辑融入其中的忠实粉丝。这可能会使支持成为问题,因为:

  • 这是一个额外的代码层来研究
  • 有时候,正如OP所了解的那样,当你需要进行数据修复时,触发器可能会做的事情是假设数据更改总是通过应用程序指令,而不是来自开发人员或DBA修复问题,甚至来自不同的应用

至于必须绕过触发器做某事,可能意味着你做错了什么,或者可能意味着触发器做错了什么。

我喜欢使用触发器的一般规则是保持它们轻便,快速,简单,并尽可能无创。

答案 5 :(得分:1)

我发现自己在进行批量数据导入时会绕过触发器。我认为在这种情况下这是合理的。

如果你最终经常绕过触发器,你可能需要先看一下你把它放在那里的东西。

一般来说,我会投票支持“它们在某些情况下有用”。我总是对性能影响感到紧张。

答案 6 :(得分:0)

总粉丝,

,但实际上必须谨慎使用它,

  • 需要保持一致性(特别是在仓库中使用维度表时,我们需要将事实表中的数据与其正确的维度相关联。有时,维度表中的正确行可能非常昂贵计算所以你希望将密钥直接写入事实表,这是维持“关系”与触发器的一种好方法。

  • 需要记录更改(例如,在审计表中,知道@@用户做了哪些更改以及何时发生更改很有用)

一些像SQL Server 2005这样的RDBMS也为你提供了CREATE / ALTER / DROP语句的触发器(这样你就可以知道是谁创建了什么表,什么时候,删除了什么列,什么时候等等。)

老实说,在这三种情况下使用触发器,我不明白为什么你需要“禁用”它们。

答案 7 :(得分:0)

我个人不是粉丝。我将使用它们,但只有当我发现代码中的瓶颈时才能通过将动作移动到触发器中来清除它。通常,我更喜欢简单,一种简单的方法是将逻辑保存在一个地方 - 应用程序。我还从事过非常划分的工作。在这些环境中,我打包的代码越多,触发即使是最简单的修复,我也需要参与的人越多。

答案 8 :(得分:0)

几周前我第一次使用触发器。我们将生产服务器从SQL 2000更改为SQL 2005,我们发现驱动程序与NText字段(存储大型XML文档)的行为不同,从最后一个字节中删除。我使用触发器作为临时修复,在数据末尾添加一个额外的虚拟字节(空格),解决了我们的问题,直到可以推出适当的解决方案。

除了这个特殊的临时案例,我会说我会避开它们,因为它们隐藏了正在发生的事情,它们提供的功能应该由开发人员明确地处理,而不是作为一些隐藏的魔法。

答案 9 :(得分:0)

老实说,我唯一一次使用触发器来模拟一个唯一的索引,该索引允许具有不计入唯一性的NULL。

答案 10 :(得分:0)

  

关于减少工作量:当数据库不必处理外部世界时,它们的效率非常高;你甚至会惊讶于甚至过程切换会伤害性能。这是存储过程的另一个好处:而不是十几次调用数据库(以及所有相关的往返),只有一个。

这有点偏离主题,但你也应该意识到你只是从一个潜在的积极看待这个。

在单个存储过程中收集内容很好,但是当出现问题时会发生什么?假设您有5个步骤,第一步失败,其他步骤会发生什么?你需要在那里添加一大堆逻辑来满足这种情况。一旦开始这样做,就会失去该场景中存储过程的好处。

答案 11 :(得分:0)

一般的经验法则是:不要使用触发器。如前所述,它们增加了开销和复杂性,可以通过将逻辑移出DB层来轻松避免。

此外,在MS SQL Server中,每个sql命令触发一次触发器,而不是每行触发一次。例如,以下sql语句将仅执行一次触发器。

UPDATE tblUsers
SET Age = 11
WHERE State = 'NY'

许多人,包括我自己,都认为每一行都会触发触发器,但情况并非如此。如果您有一个类似上面的sql语句可能会更改多行中的数据,您可能希望包含一个游标来更新受触发器影响的所有记录。你可以很快看到它如何变得复杂。