数据库触发器是邪恶的吗? [关闭]
Posted
技术标签:
【中文标题】数据库触发器是邪恶的吗? [关闭]【英文标题】:Are database triggers evil? [closed] 【发布时间】:2010-10-02 09:33:39 【问题描述】:数据库触发器是个坏主意吗?
根据我的经验,它们是邪恶的,因为它们会导致令人惊讶的副作用,并且难以调试(尤其是当一个触发器触发另一个触发器时)。通常,开发人员甚至不会考虑查看是否有触发器。
另一方面,如果您的逻辑必须在每次在数据库中创建新的FOO
时发生,那么放置它的最简单的地方就是在 FOO 表上插入触发器。
我们唯一一次使用触发器是为了非常简单的事情,比如设置ModifiedDate
。
【问题讨论】:
这是一个完全合理的问题,但我不太喜欢耸人听闻的标题。我认为类似“实现数据库触发器时要考虑的最重要问题是什么?”会好很多。 该问题已关闭以添加答案,但另请参阅Are database triggers safe for cross table integrity constraints?。 (剧透:不,他们不是) 这个网站让我很生气。这是一个 GREAT 问题,但与许多其他问题一样,它已关闭,因为人们缺乏想象力来接受不适合 Q&A 的原始二进制格式的问题,他们出于某种奇怪的原因感到不得不遵循。 触发器中的业务逻辑是有问题的(如果你愿意的话,这是邪恶的)。触发器中的数据库逻辑没有问题(完整性、日志记录)。 @Quibblesome 我同意。这个问题与我目前正在做的事情有关。而人们太容易跳到***上关闭问题。很棒的是,这个页面首先出现在我的谷歌搜索中,所以它仍然可以访问并被认为是相关的。 【参考方案1】:如果有副作用,那是设计问题。 在某些数据库系统中,没有其他可能设置自动增量字段,即为主键 ID 字段。
【讨论】:
【参考方案2】:不,它们实际上是个好主意。如果您的特定触发器有问题,那么您没有正确执行它们,但这通常意味着您的实现存在问题,不是触发器本身的概念:-)。
我们大量使用触发器,因为它将特定于 DBMS 的活动置于其所属数据库的控制之下。 DBMS 的用户不必担心这类事情。数据的完整性在于数据库本身,而不是使用它的应用程序或用户。如果数据库中没有约束、触发器和其他功能,则留给应用程序执行规则,并且只需要一个流氓或错误的应用程序/用户来破坏数据。
例如,如果没有触发器,诸如自动生成的列之类的奇妙事物将不存在,您必须在选择它们时对每一行处理一个函数。这可能会降低 DBMS 的性能,最好在插入/更新时创建自动生成的列,因为这是它唯一更改的时间。
此外,缺少触发器会阻止在 DBMS 中强制执行数据规则,例如预触发器以确保列具有特定格式。请注意,这与通常只是外键查找的数据完整性规则不同。
【讨论】:
"在选择每一行时处理一个函数"。为此目的,最好使用基于函数的索引而不是触发器。 不一定,触发器可能只会在插入或更新行时运行。基于函数的索引将为每个选择运行。根据使用模式,一种可能比另一种更好。但两者都不总是比另一个更好。 @tuinstoel:我不得不部分同意你的说法。例如,Oracle 只会创建基于函数的索引,前提是它可以证明函数是确定性的。有时无法证明这一点(例如,如果函数涉及从表中查找,即使您知道表的数据永远不会改变)。【参考方案3】:触发器的主要问题是
它们是完全全局的 - 无论表活动的上下文如何,它们都适用; 它们是隐身的;很容易忘记它们的存在,直到它们以意想不到(而且非常神秘)的后果伤害您。这只是意味着它们需要在适当的情况下小心使用;以我的经验,这仅限于关系完整性问题(有时粒度比您可以通过声明获得的更精细);通常不用于商业或交易目的。 YMMV。
【讨论】:
在某些情况下,这是两个优势。 “隐身”是个好词,是的——说得好。这正是我倾向于回避它们的原因:它们经常被遗忘或忽视。以我个人的经验,重新审视触发器通常伴随着我自己的额头。 全球性是为什么它们对于数据完整性和审计等事情是好的和必要的。这不是减号,而是加号。 所以@RobertŠevčík-Robajz,你是说你认识的所有开发人员都不称职? @HGLEM,同意应该有专家来制定触发器。现实生活中的场景 - 没有。现实生活场景 - 试图识别与被遗忘的触发器相关的错误所花费的天数。现实生活场景——触发逻辑被拼命地推入应用程序逻辑中,在那里它可以很容易地重构和单元测试。我所处理的现实生活让我说“远离触发器”......这不是触发器的错,因为窗户被打破不是石头的错。【参考方案4】:确实,触发器经常被滥用。实际上,在大多数情况下,您甚至不需要它们。但这并不意味着它们一定是坏的。
我想到的一个触发器很有用的场景是,当您有一个遗留应用程序但您没有源代码并且无法更改它时。
【讨论】:
【参考方案5】:我同意。触发器的问题在于人,而不是触发器。尽管它需要更多地查看、更多地考虑并增加编码人员正确检查事物的责任,但我们不会丢弃索引以使我们的生活更简单。 (坏索引可能和坏触发器一样坏)
触发器的重要性(在我看来)是...... - 任何系统都应始终处于有效状态 - 执行这个有效状态的代码应该是集中的(不是写在每个 SP 中)
从维护的角度来看,触发器对有竞争力的编码人员非常有用,而对于更多初级/业余人员来说,触发器是非常有用的。然而,这些人需要以某种方式学习和成长。
我想这取决于您的工作环境。您是否有可靠的人,他们学得很好并且可以相信有条不紊?如果不是,您似乎有两个选择: - 接受你将不得不失去功能来补偿 - 接受你需要不同的人或更好的培训和管理
它们听起来很刺耳,我猜是这样。但在我看来,这是基本事实......
【讨论】:
>>>触发器的问题在于人。是的,如果人们能够在汇编中编写代码,使用糟糕的 GUI,正确猜测是推还是拉设计糟糕的门……人们反复犯错的任何“功能”都是“邪恶的”。 @Fakrudeen,任何触发错误的开发人员都无法访问数据库。【参考方案6】:大多数情况下,是的。
触发器的困难在于它会“在你背后”做一些事情;维护应用程序的开发人员很容易没有意识到它的存在并做出更改,甚至在没有注意到的情况下搞砸了。
它创建了一层复杂性,只会增加维护工作。
通常可以使存储过程/例程而不是使用触发器来执行相同的操作,但以清晰且可维护的方式 - 调用存储例程意味着开发人员可以查看其源代码并准确了解其内容正在发生。
【讨论】:
这是触发器的优点而不是缺点!不能保证每次更改数据都会调用存储过程。除了 GUI 之外,还有很多方法可以更改数据。 HLGEM,这取决于您的访问控制。除了通过存储过程之外,您可以直接拒绝对表的任何修改。 我认为关键是,例如,如果两个表中的记录应该始终一起创建和销毁,无论您如何访问数据库,无论您是谁或拥有什么权限,那么触发器是唯一合法的解决方案。甚至可能分配太多或不正确的权限并期望人们知道要使用哪些存储过程这一事实,意味着数据库有失去其完整性的风险。它与外键关系完全相同。它是由数据库引擎执行的最好和最可靠的。 如果记录应该始终一起创建/销毁,请创建一个检查约束以确保它们是。这样一来,违反规则的人就会犯错,而不是在他们不知情或不同意的情况下神奇地使事情变得正确的隐藏行为。【参考方案7】:我知道那些认为触发器应该始终用于实现他们想要的功能的最直接方式的开发人员,而开发人员永远不会。这几乎就像两个阵营之间的教条。
但是我个人完全同意 MarkR - 您可以(几乎)始终编写功能上与触发器等效的代码,这样会更明显,因此更易于维护。
【讨论】:
除了不是所有打数据库的工作都流经应用程序代码。【参考方案8】:不邪恶。他们实际上简化了诸如
之类的事情1.记录/审核记录甚至数据库架构的更改
您可以在 ALTER TABLE 上设置一个触发器来回滚生产环境中的更改。这应该可以防止任何意外的表修改。
2.跨多个数据库强制执行参照完整性(主/外键关系等)
【讨论】:
可以回滚 DDL 语句吗? 一般不会。阻止这种情况的唯一方法是从用户登录中删除该权限。 在某些数据库引擎中可以(例如 PostgreSQL)。 @Andrew - 在 SQL Server 中你可以。 SQL Server 2005+ 还具有 DDL 触发器,可在ALTER TABLE
等事件上触发。【参考方案9】:
触发器非常强大和有用,在许多情况下触发器是问题的最佳解决方案。
它们也是一个非常好的“破解”工具。在某些情况下,您无法立即控制代码和数据库。如果您必须等待 2 个月的代码的下一个主要版本,但您可以立即将补丁应用到您的数据库,那么您可以在表上放置一个触发器以执行一些额外的功能。然后,当可以发布代码时,如果需要,您可以用您的相同功能的编码版本替换此触发器。
归根结底,如果您不知道它在做什么,那么一切都是“邪恶的”。决定触发器是因为有些开发人员不了解它们,就等于认为汽车是邪恶的,因为有些人不会开车......
【讨论】:
【参考方案10】:我认为它们可能是邪恶的,但与开发中的其他任何东西一样邪恶。
虽然我对它们并没有太多经验,但我确实在我最近参与的一个项目中使用了它们,这让我得出了这个结论。我遇到的问题是它们可能导致业务逻辑最终出现在两个位置,一个代码库和一个数据库。
我认为它与使用存储过程类似。您通常会有非常擅长 SQL 将业务逻辑写入数据库的开发人员,而不擅长的开发人员则将他们的业务逻辑放在其他地方。
所以我的经验法则是看看你的项目的结构是什么。如果将业务逻辑存储在数据库中似乎可行,那么拥有触发器可能会很有用。
【讨论】:
【参考方案11】:不,他们不是邪恶的——他们只是被误解了:-D
触发器有一个有效的用途,但往往作为一种复古黑客,最终使事情变得更糟。
如果您将数据库作为应用程序的一部分进行开发,则逻辑应始终位于进行调用的代码或存储过程中。触发器只会在以后导致调试痛苦。
如果您了解锁定、死锁以及 DB 如何访问磁盘上的文件,那么以正确的方式使用触发器(例如审核或归档直接 DB 访问)会非常有价值。
【讨论】:
【参考方案12】:说他们是邪恶的有点夸大其词,但他们可能会导致网状结构。当一个触发器的触发导致其他触发器触发时,它变得非常复杂。假设他们很麻烦:http://www.oracle.com/technology/oramag/oracle/08-sep/o58asktom.html
由于多并发问题,在 Oracle 中使用触发器执行业务逻辑比看起来更难。在其他会话提交之前,您不会在其他会话中看到更改。
【讨论】:
【参考方案13】:触发器有它们的用途 - 记录/审计和维护“最后修改”日期是之前的回复中提到的两个非常好的用途。
但是,良好设计的核心原则之一是业务规则/业务逻辑/无论您想如何称呼它都应该集中在一个地方。将一些逻辑放在数据库中(通过触发器或存储过程)和一些放在应用程序中违反了该原则。在这两个地方重复逻辑更糟糕,因为它们总是会彼此不同步。
还有已经提到的“最小意外原则”问题。
【讨论】:
没错,它应该在一个地方,数据库。影响数据完整性的逻辑必须始终存在于数据库中,而决不能存在于应用程序中,当影响数据库中的数据时,它可能会被调用,也可能不会被调用。 @HLGEM:这取决于数据库是否可以访问允许它判断数据是否有效的信息。并非总是如此。当验证者在另一个组织中时(例如,用于信用卡或银行帐户详细信息),那么数据库就无法知道它是否正确——假设这不是银行的数据库! ——它必须依靠申请来强制执行。您不希望让数据库随机连接到第三方服务,因为这在服务器部署方面很糟糕。 @HLGEM:虽然我还没有准备好完全排除将所有应用程序逻辑放入数据库的选项,但我发现将其放在其他地方会更好,通常是可重用的 OO 层可用于所有访问数据库的应用程序。只要您的应用程序仅通过对象层访问数据库,始终调用逻辑的相同保证仍然适用。 从未开发过只通过对象层将数据插入数据库的业务应用程序,我也不想开发一个。通过一个旨在一次只处理一个记录的过程来进行数百万记录的导入或所有价格的更新是愚蠢的。对象层完全是执行数据完整性的错误场所,这就是为什么这么多数据库存在完整性问题的原因。 @HLGEM 正是出于这个原因,我正在对我们的 ORM 进行扩展,以便像触发器一样使用事务中所有内容的变更集。这感觉有点傻,但会阻止我们在应用程序中拥有所有业务逻辑,除非有几次不是(只有少数表需要批量更新)。它还将允许所有开发人员以他们最熟悉的语言编写和使用它们,并且可以访问我们构建的所有对象抽象。【参考方案14】:工具从不邪恶。 这些工具的应用可能是邪恶的。
【讨论】:
我在阅读评论后从未如此矛盾。一方面,我支持第二修正案,并相信枪支本质上并不是邪恶的:它是使用它们的人。另一方面,我认为触发器是邪恶的......我认为我正在经历一场存在主义的崩溃...... @vbullinger 枪并不邪恶,但它们的触发器是;) :D 概括是危险的(递归的)。你是不是通过审问者用来“触发”认罪的酷刑“工具”来的?无论如何都要为视角+1。【参考方案15】:正确使用触发器是一个很好的工具。尤其适用于审核更改、填充汇总表等。
现在,如果您最终进入“触发地狱”,其中一个触发触发了其他触发,那么它们可能是“邪恶的”。我曾经在一个 COTS 产品上工作过,他们有所谓的“弹性触发器”。这些触发器存储在一个表中,因为动态 sql 字符串在每次执行时都会被编译。编译的触发器会进行查找,看看该表是否有任何弹性触发器要运行,然后编译并运行“弹性”触发器。从理论上讲,这听起来是一个非常酷的想法,因为该产品很容易定制,但现实情况是数据库由于它必须进行的所有编译而几乎爆炸......
所以,是的,如果你能正确看待你正在做的事情,它们会很棒。如果它是非常简单的事情,比如审计、总结、自动排序等,那就没有问题了。请记住表的增长率以及触发器将如何影响性能。
【讨论】:
【参考方案16】:我认为触发器不仅不是邪恶的,而且是良好的数据库设计所必需的。应用程序程序员认为数据库只受其应用程序的影响。他们经常是错的。如果要保持数据完整性,无论数据来自何处,触发器都是必需的,避免它们是愚蠢的,因为一些程序员过于以民族为中心,认为除了他们珍贵的应用程序之外的其他东西可能会影响事物。如果您是一位称职的数据库开发人员,那么设计、测试或排除触发器故障并不难。也很难确定触发器是否会导致意外结果,如果它发生在你身上(就像它对我一样)看那里。如果我收到一条错误消息,指出我没有在我的 sp 中引用的表有一个 FK 错误,我什至不考虑它就知道是触发器导致了问题,任何有能力的数据库开发人员也应该如此。仅将业务规则放在应用程序中是我发现不良数据的首要原因,因为其他人甚至不知道规则存在并在他们的流程中违反它。以数据为中心的规则属于数据库,触发器是执行更复杂规则的关键。
【讨论】:
以数据为中心的规则属于数据库 有我some programmers are too ethnocentric to consider that something other than their prized application may be affecting things
【参考方案17】:
触发器的想法不是邪恶的,限制触发器的嵌套是邪恶的。
【讨论】:
【参考方案18】:他们绝对不是邪恶的。我发现在重构数据库模式、重命名列或将列拆分为两列或反之亦然(例如:姓名/姓氏大小写)并协助转换时,触发器很重要。
它们对于审计也非常有用。
【讨论】:
【参考方案19】:此答案专门适用于 SQL Server。 (尽管它也可能适用于我不知道的其他 RDBMS。我更愿意将其作为答案 here 但这已被关闭作为一个骗局。)
到目前为止,任何答案中都没有提到一个方面是安全性。因为,默认情况下,触发器在执行导致触发器触发的语句的用户的上下文中执行,这可能会导致安全威胁,除非所有触发器都经过审查。
BOL 中“Managing Trigger Security”标题下给出的示例是用户创建包含代码GRANT CONTROL SERVER TO JohnDoe ;
的触发器以提升自己的权限。
【讨论】:
【参考方案20】:在高层次上,触发器有两个用例1
1) 让事情“自动”发生。在这种情况下,触发器会产生副作用,它们会以不期望的方式更改数据,因为执行了(原始)运算符插入、更新或删除并导致触发器触发。
这里的普遍共识是触发器确实有害。因为它们改变了众所周知的 INSERT、UPDATE 或 DELETE 语句的语义。更改这三个原语 SQL 运算符的语义会影响其他开发人员,他们以后需要处理您的数据库表,当使用 SQL 原语对其进行操作时,这些表的行为不再符合预期。
2) 强制执行数据完整性规则,而不是我们可以以声明方式处理的规则(使用 CHECK、PRIMARY KEY、UNIQUE KEY 和 FOREIGN KEY)。在这个用例中,所有触发器所做的都是查询(SELECT)数据,以验证是否允许 INSERT/UPDATE/DELETE 所做的更改。就像声明性约束对我们所做的一样。只有在这种情况下,我们(开发人员)才对执行进行了编程。
对后一个用例使用触发器是无害的。
我在写博客:http://harmfultriggers.blogspot.com
【讨论】:
当使用触发器来实现引用完整性时,处理并发问题比看起来更难。 同意。但是使用其他方式会更容易吗? 虽然有很多不称职的开发人员。 我不同意触发器是有害的。如果您确切地知道触发器在做什么,并且您对其进行了良好的编程,那么它应该始终按预期工作。这里唯一的痛点是不准确地实现或使用它。以上是关于数据库触发器是邪恶的吗? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
用PLSQL导数据库数据,用Tools--Export Tables 导出 dmp文件,其中包括存储过程和触发器,函数等的吗?