触发器、断言和检查(在数据库中)有什么区别?

29

有人能解释一下(或者推荐一个网站或论文),触发器(triggers)、断言(assertions)和检查(checks)之间的确切区别,并描述何时应该使用它们吗?

编辑:我指的是在数据库中,而不是其他任何系统或编程语言中。


http://technet.microsoft.com/sqlserver/? - John Saunders
5个回答

65

触发器 - 触发器是在数据库中执行更新、插入或删除操作前后要执行的一条SQL语句。一个简单英语例子可能是:在更新客户记录之前,保存当前记录的副本。这可能看起来像:

CREATE TRIGGER triggerName
AFTER UPDATE
    INSERT INTO CustomerLog (blah, blah, blah)
    SELECT blah, blah, blah FROM deleted
断言和检查之间的区别有点模糊,许多数据库甚至不支持断言。 检查约束 - 检查是一段SQL代码,它确保在对记录执行操作之前满足某个条件。用简单的英语来说,就是:所有客户的账户余额必须至少为100美元。这将类似于:
ALTER TABLE accounts 
ADD CONSTRAINT CK_minimumBalance
CHECK (balance >= 100)

如果尝试在余额列中插入小于100的值,将会抛出错误。

断言 - 断言是一条SQL语句,用于确保满足某个条件或停止对数据库对象进行操作。它可能意味着锁定整个表甚至整个数据库。

更加令人困惑的是 - 触发器可以用来执行检查约束,在某些数据库中可以取代断言(通过允许您运行与修改表无关的代码)。初学者常犯的一个错误是在需要触发器时使用检查约束,或者在需要检查约束时使用触发器。

例如:所有新客户开设账户必须有100美元的余额;然而,一旦账户开设成功,他们的余额可以低于该金额。在这种情况下,您必须使用触发器,因为您只想在插入新记录时评估条件。


15
在SQL标准中,ASSERTIONS 和CHECK CONSTRAINTS都被称为"约束条件",它们是指数据库中实际包含的数据必须符合的规则。
两者之间的区别在于,CHECK CONSTRAINTS更加简单,它们只涉及单个行,而ASSERTION可以涉及任意数量的其他表或同一表中的任意数量的其他行。这使得DBMS构建者的支持变得更加复杂,因此他们不能做到:他们不知道如何做到。
TRIGGER是可执行代码片段,可以声明给DBMS,在特定表上进行某种更新操作(插入/删除/更新)时,应该执行这些代码片段。由于触发器可以引发异常,因此它们是实现与ASSERTION相同功能的一种手段。但是,使用触发器时,仍然需要程序员自己编写所有代码,并且不能出错。
编辑:
关于ASSERTION/CHECK CONSTRAINTS,Onedaywhen的评论是正确的。两者之间的差异更加微妙(和混淆)。标准确实允许在CHECK CONSTRAINTS中使用子查询。(大多数产品不支持它,因此我的“只与单个行相关”对于大多数SQL产品来说是正确的,但对于标准来说不是这样。)那么还有区别吗?是的,还有一个以上的区别。第一种情况是:表MEN(ID:INTEGER)和WOMEN表(ID:INTEGER)。现在想象一个规则,即“ID值不能同时出现在MEN表和WOMEN表中”。这是一个单独的规则。ASSERTION的目的是让数据库设计人员说明这个单个规则[并完成它],并且DBMS将知道如何处理这个规则[当然要高效]以及如何强制执行此规则,无论对数据库进行了什么特定的更新。在这个例子中,DBMS将知道必须在插入到MEN表和WOMEN表时检查此规则,但不必检查从MEN / WOMEN表中删除或插入到任何其他表中。
但是,DBMS并不聪明到可以做到这一点。那么需要做什么?数据库设计人员必须向他的数据库添加两个CHECK约束,一个用于MEN表(检查新插入的MEN ID是否与WOMEN表匹配),另一个用于WOMAN表(反向检查)。这就是第一点不同之处:一个规则,一个ASSERTION,两个CHECK约束。CHECK约束是比ASSERTION更低级别的抽象,因为它要求设计人员自己考虑更多关于可能导致断言被违反的更新类型以及应对任何特定“更新类型”所需的具体检查的问题。虽然我不喜欢在仍然是“WHAT”和“HOW”的领域上做出“绝对”的陈述,但我想总结一下CHECK约束需要数据库设计人员进行更多的“HOW”思考(过程性),而ASSERTION允许数据库设计人员专注于“WHAT”(声明性)。第二种情况(虽然我不是完全确定,所以请谨慎对待):这是一个普通的RI规则。当然,你通常会使用某些REFERENCES子句来指定这个规则。但是想象一下,如果没有REFERENCES子句可用,那么像“每个订单必须由已知客户下单”这样的规则实际上就是一个单独的ASSERTION。然而,我们都知道这样的规则总是可以通过两种方式违反:在插入一个订单时(以此为例),或者删除一个客户。现在,根据上面的MAN/WOMEN示例,如果我们想使用CHECK约束来实现这个单个规则/ASSERTION,那么我们需要编写一个CHECK约束,以检查在将订单插入到表中时CUSTOMER是否存在,但是在从CUSTOMER中删除数据时,我们该如何编写能够有效检查的CHECK约束呢?就我所知,它们并不是为这个目的而设计的。这里就是第二个区别:CHECK约束仅与插入相关联,而ASSERTIONS可以定义规则,在DELETE时也会进行检查。
第三种情况:假设有一个COMPOS表(componentID:... percentage:INTEGER),其中有一个规则,即“所有百分比的总和必须始终等于100”。这是一个单一的规则,可以使用ASSERTION指定。但是请想象一下,如果想要使用CHECK约束来强制实施这样的规则,你该如何操作?如果你有一个有效的表,其中有三行非零百分比之和等于100,那么你如何应用任何更改才能使它通过你的CHECK约束?你无法删除或更新(减少)任何行而不必添加其他替换行,或更新其余行,使它们总和相等。同样,对于插入或更新(增加),也是如此。你至少需要延迟约束检查,但那么你将要检查什么?这里就是第三个区别:CHECK约束针对单个行,而ASSERTION可以定义/表示涉及多行的规则(即关于行汇总的规则)。

“CHECK”约束并不仅“涉及单个行”:它们可以包含子查询以引用多个行,包括声明“CHECK”的表之外的表中的行。实际上,区别在于当更新声明了“CHECK”的表时,只有“CHECK”才会被测试,而“ASSERTION”无论涉及到更新的表如何都会被测试。 - onedaywhen
1
当然,高效是前提。如果高效不是要求的一部分,甚至我的猫都知道“如何做到”。 - Erwin Smout
你对CHECK约束的想法是正确的。欢迎对我的修改提出任何意见。 - Erwin Smout
1
有趣的是,你、我、作者...都为这个SO问题提供了答案!你可以打赌会有更多这样的问题。(如果Stackoverflow支持关系除法,我们就可以轻松搜索它们!!!!)Relationland不是一个很大的游泳池,游泳者也不太多...(而David肯定是其中更值得尊敬的人之一。) - Erwin Smout
1
我对您关于CHECK约束的进一步评论感到很好奇。标准中似乎确实没有说明CHECK是针对单个行级别进行的。奇怪的是,也没有明确说明CHECK是针对表级别进行的!!!!最确定的是,在CHECK约束表达式的上下文中有时可用的结构(例如Oracle的:new.<columnname>)绝对是针对行级别的,并且绝对与针对表级别不兼容)。... - Erwin Smout
显示剩余6条评论

7

断言并不修改数据,它们只检查特定的条件

触发器更加强大,因为它们可以检查条件并且可以修改数据


断言与数据库中的特定表格以及特定事件无关

触发器与特定的表格和特定的事件相关联


谢谢。"未链接到特定表的触发器"和"链接到特定表的触发器"是什么意思? - user3284469

3

数据库约束是指在更新数据库时必须满足的条件。在SQL中,如果约束条件计算结果为假,则更新失败,数据保持不变,并且DBMS会生成错误。

CHECKASSERTION两者都是SQL标准定义的数据库约束。一个重要的区别是,CHECK适用于特定的基本表,而ASSERTION适用于整个数据库。考虑一个限制表T1T2中行的合计数不超过10行的约束条件。例如:

CHECK (10 >= (
              SELECT COUNT(*)
                FROM (
                      SELECT *
                        FROM T1
                      UNION
                      SELECT * 
                        FROM T2
                     ) AS Tn
             ))

假设表为空。如果这只是应用为一个 ASSERTION ,并且用户尝试将 11 行插入 T1,则更新将失败。如果将约束作为 CHECK 约束应用于仅 T1,则情况相同。但是,如果将约束作为 CHECK 约束仅应用于 T2,则约束将成功,因为针对 T1 的语句不会导致对 T1 应用的约束被测试。
ASSERTION 和 CHECK 都可以推迟(如果声明为 DEFERRABLE),允许数据在事务中暂时违反约束条件。
涉及子查询的 ASSERTION 和 CHECK 约束是标准 SQL 之外的功能,没有任何主要的 SQL 产品支持这些功能。MS Access(不是一个工业强度的产品)支持涉及子查询的 CHECK 约束,但不支持可推迟的约束,而且约束测试总是以逐行方式执行,实际影响是功能非常有限。
与 CHECK 约束类似,触发器应用于特定表。因此,触发器可以用于实现与 CHECK 约束相同的逻辑,但不能用于 ASSERTION。触发器是过程代码,与约束不同,用户必须承担更多的性能和错误处理等方面的责任。大多数商用 SQL 产品都支持触发器(前面提到的 MS Access 不支持)。

一个CHECK应用于特定的基础表,而CHECK约束应用于整个数据库,我认为其中一个CHECK必须替换为Assertion。 - Am1rr3zA

0

表达式必须为真才能触发,但如果表达式为假,检查将在任何地方进行评估。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接