18

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

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

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

4

12 回答 12

12

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

Oracle 副总裁Tom Kyte表示,他更愿意将触发器作为 Oracle数据库的一个特性来删除,因为它们经常在 bug 中发挥作用。他知道这只是一个梦想,触发器会一直存在,但如果他可以的话,他会从 Oracle 中删除触发器,他会(连同 WHEN OTHERS 子句和自主事务)。

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

问题是 - 它们在很多情况下都没有正确使用,以至于我愿意放弃任何感知到的好处,只是为了摆脱它们造成的滥用(和错误)。- 汤姆凯特

于 2008-08-18T03:23:39.497 回答
9

把数据库想象成一个很大的对象——在每次调用它之后,它应该处于逻辑一致的状态。

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

触发器的缺点是任何动作都可以调用它们;这也是一种优势——没有人会因为无能而破坏系统的完整性。

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

至于减少工作量:数据库在不必与外部世界打交道时效率惊人;您会非常惊讶甚至进程切换会损害性能。这是存储过程的另一个优点:而不是对数据库的十几个调用(以及所有相关的往返),有一个。

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

业务逻辑必须去某个地方,并且在数据库的设计中嵌入了许多隐含的域规则——关系、约束等是对业务规则进行编码的一种尝试,例如,用户只能有一个密码。假设您已经开始通过这些关系等将业务规则推送到数据库服务器上,那么您在哪里划清界限?数据库何时放弃对数据完整性的责任,并开始信任调用应用程序和数据库用户来正确处理数据?嵌入了这些规则的存储过程可以将大量政治权力推到 DBA 手中。这取决于您的 n 层架构中将存在多少层;如果有表示层、业务层和数据层,业务和数据的分离在哪里?业务层增加了什么附加值?您会将数据库服务器上的业务层作为存储过程运行吗?

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

在此处输入图像描述

于 2008-08-18T00:37:45.760 回答
2

我在 c# 中使用 web 和 winforms 应用程序,我非常讨厌触发器。我从来没有遇到过这样的情况,我可以证明使用触发器而不是将该逻辑移动到应用程序的业务层并在那里复制触发器逻辑。

我不做任何 DTS 类型的工作或类似的工作,因此可能会有一些使用触发器的用例,但如果我的任何团队中的任何人说他们可能想要使用触发器,他们最好已经准备好他们的论点因为我拒绝袖手旁观,让触发器添加到我正在处理的任何数据库中。

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

  • 他们将逻辑移动到数据库中。一旦你开始这样做,你就要求一个痛苦的世界,因为你失去了你的调试、你的编译时间安全和你的逻辑流程。都是下坡路。
  • 他们实现的逻辑对任何人来说都不容易看到。
  • 并非所有数据库引擎都支持触发器,因此您的解决方案会创建对数据库引擎的依赖关系

我敢肯定我能想到更多的原因,但仅凭这些就足以让我不使用触发器。

于 2008-08-18T00:08:53.853 回答
2

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

但我不喜欢将大量业务逻辑放入其中。这可能会使支持出现问题,因为:

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

至于必须绕过触发器来做某事,这可能意味着您做错了什么,也可能意味着触发器做错了什么。

我喜欢与触发器一起使用的一般规则是让它们保持轻便、快速、简单和尽可能非侵入性。

于 2008-08-18T00:18:13.863 回答
2

"Never design a trigger to do integrity constraint checking that crosses rows in a table" -- I can't agree. The question is tagged 'SQL Server' and CHECK constraints' clauses in SQL Server cannot contain a subquery; worse, the implementation seems to have a 'hard coded' assumption that a CHECK will involve only a single row so using a function is not reliable. So if I need a constraint which does legitimately involve more than one row -- and a good example here is the sequenced primary key in a classic 'valid time' temporal table where I need to prevent overlapping periods for the same entity -- how can I do that without a trigger? Remember this is a primary key, something to ensure I have data integrity, so enforcing it anywhere other than the DBMS is out of the question. Until CHECK constraints get subqueries, I don't see an alternative to using triggers for certain kinds of integrity constraints.

于 2008-10-14T19:38:45.357 回答
1

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

如果你最终经常绕过触发器,你可能需要再看看你把它们放在那里是为了什么。

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

于 2008-08-18T00:13:45.310 回答
0

就个人而言,我不是粉丝。我将使用它们,但只有当我发现代码中的瓶颈可以通过将操作移入触发器来清除时。一般来说,我更喜欢简单,而保持简单的一种方法是将逻辑保存在一个地方——应用程序。我还从事过访问非常分散的工作。在这些环境中,我打包的代码越多,我就必须让越多的人参与进来,即使是最简单的修复也是如此。

于 2008-08-18T00:09:32.510 回答
0

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

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

于 2008-08-18T00:30:46.210 回答
0

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

于 2008-08-18T00:40:58.123 回答
0

至于减少工作量:数据库在不必与外部世界打交道时效率惊人;您会非常惊讶甚至进程切换会损害性能。这是存储过程的另一个优点:而不是对数据库的十几个调用(以及所有相关的往返),有一个。

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

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

于 2008-08-18T01:01:10.017 回答
0

总粉丝,

但真的必须谨慎使用它,

  • 需要保持一致性(尤其是在仓库中使用维度表时,我们需要将事实表中的数据与其适当的维度相关联。有时,维度表中的适当行的计算成本可能非常高,因此您需要密钥要直接写入事实表,保持“关系”的一种好方法是使用触发器。

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

一些 RDBMS 像 sql server 2005 还为您提供了 CREATE/ALTER/DROP 语句的触发器(这样您就可以知道谁创建了什么表、何时、删除了哪些列、何时等。)

老实说,在这 3 个场景中使用触发器,我不明白你为什么需要“禁用”它们。

于 2008-08-18T01:32:48.143 回答
0

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

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

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

许多人,包括我自己,都认为触发器是在每一行触发的,但事实并非如此。如果您有一个像上面这样的 sql 语句可能会更改多行中的数据,您可能希望包含一个游标来更新受触发器影响的所有记录。您可以看到这如何很快变得令人费解。

于 2008-08-18T19:08:10.387 回答