【问题标题】:what is the difference between triggers, assertions and checks (in database)触发器、断言和检查之间有什么区别(在数据库中)
【发布时间】:2020-11-07 21:38:15
【问题描述】:

谁能解释(或推荐一个网站或论文)触发器、断言和检查之间的确切区别,并描述我应该在哪里使用它们?

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

【问题讨论】:

标签: sql database triggers assertions


【解决方案1】:

触发器 - 触发器是在数据库中更新、插入或删除之前或之后执行的一段 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,可确保满足条件或停止对数据库对象执行操作。这可能意味着锁定整个表甚至整个数据库。

为了让事情变得更加混乱 - 触发器可用于强制检查约束,并且在某些 DB 中可以代替断言(通过允许您运行与正在修改的表无关的代码)。初学者的一个常见错误是在需要触发器时使用检查约束或在需要检查约束时使用触发器。

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

【讨论】:

    【解决方案2】:

    在 SQL 标准中,ASSERTIONS 和 CHECK CONSTRAINTS 都是关系理论所说的“约束”:数据库中实际包含的数据必须遵守的规则。

    两者之间的区别在于 CHECK CONSTRAINTS 在某种意义上更“简单”:它们是仅与单行相关的规则,而 ASSERTION 可以涉及任意数量的其他表或任意数量的其他行在同一张桌子上。这显然使 DBMS 构建者支持它(非常!)变得更加复杂,而这反过来也是他们不支持它的原因:他们只是不知道如何去做。

    TRIGGER 是可执行代码片段,可以向 DBMS 声明,每次对某个表执行某种更新操作(插入/删除/更新)时,都应该执行这些代码片段。因为触发器可以引发异常,所以它们是实现与断言相同的事物的一种手段。但是,使用触发器,仍然是程序员必须完成所有编码,并且不会犯任何错误。

    编辑

    Onedaywhen 的 cmets re.断言/检查 cnstr。是正确的。区别更加微妙(并且令人困惑)。该标准确实允许 CHECK 约束中的子查询。 (虽然大多数产品不支持它,所以我的“与单行相关”对于大多数 SQL 产品都是正确的,但不适用于标准。)那么还有区别吗?是的,还有。不止一个。

    第一种情况:TABLE MEN (ID:INTEGER) 和 TABLE WOMEN(ID:INTEGER)。现在想象一条规则,大意是“在 MEN 表和 WOMEN 表中都不能出现 ID 值”。这是一个单一的规则。 ASSERTION 的意图恰恰是数据库设计者会陈述这个单一的规则[并完成它],并且 DBMS 会知道如何处理这个[当然是有效的]以及如何执行这个规则,无论什么特别对数据库进行更新。在示例中,DBMS 会知道它必须在 INSERT INTO MEN 和 INSERT INTO WOMEN 时检查此规则,而不是在 DELETE FROM MEN/WOMEN 或 INSERT INTO 时检查。

    但 DBMS 还不够聪明,无法完成所有这些工作。那么需要做什么呢?数据库设计者必须将两个 CHECK 约束添加到他的数据库,一个到 MEN 表(检查新插入的 MEN ID 与 WOMEN 表)和一个到 WOMAN 表(检查相反的方式)。您的第一个区别是:一个规则,一个 ASSERTION,TWO CHECK 约束。 CHECK 约束是比 ASSERTION 更低的抽象级别,因为它们要求设计者自己做更多的思考(a)可能导致其 ASSERTION 被违反的所有类型的更新,以及(b)应该进行哪些特定检查他在 (a) 中发现的任何特定“更新类型”。 (虽然我不太喜欢就什么是“什么”和什么是“如何”做出“绝对”的陈述,但我总结一下,检查约束需要数据库设计者更多的“如何”思考(程序),而断言允许数据库设计者专注于“WHAT”(声明性)。)

    第二种情况(尽管我对此并不完全确定 - 所以请谨慎对待):只是您的平均 RI 规则。当然,您习惯于使用一些 REFERENCES 子句来指定它。但是想象一下,REFERENCES 子句不可用。像“每个订单必须由已知客户下达”这样的规则实际上就是这样一个规则,因此:一个单一的断言。然而,我们都知道这样的规则总是可以通过两种方式被违反:插入一个 ORDER(在这个例子中),和删除一个 CUSTOMER。现在,根据前面的 MAN/WOMEN 示例,如果我们想使用 CHECK 约束来实现这个单一规则/ASSERTION,那么我们必须编写一个 CHECK 约束来检查 CUSTOMER 在插入 ORDER 时是否存在,但是什么 CHECK 约束可以我们写的是在 deletion 从 CUSTOMER 时需要做的任何事情?据我所知,它们根本不是为此目的而设计的。您的第二个区别是:CHECK 约束仅与 INSERT 相关联,ASSERTIONS 可以定义也将在 DELETE 时检查的规则。

    第三种情况:想象一个表 COMPOS (componentID:... percent:INTEGER),以及“所有百分比的总和必须始终等于 100”的规则。这是一个单一的规则,并且一个 ASSERTION 能够指定它。但是试着想象一下你将如何使用 CHECK 约束来执行这样的规则......如果你有一个有效的表,例如,三个非零行加起来一百,你将如何对这个表应用任何可以生存的更改你的 CHECK 约束?您不能删除或更新(减少)任何行,而不必添加其他替换行或更新剩余的行,总和相同的百分比。同样对于插入或更新(增加)。您至少需要延迟约束检查,然后您要检查什么?还有第三个区别:CHECK 约束针对单个行,而 ASSERTION 还可以定义/表达“跨越”多行的规则(即关于行聚合的规则)。

    【讨论】:

    • CHECK 约束不“仅与单行相关”:它们可能包含子查询来引用多行,包括表中的行,而不是声明CHECK 的表中的行。实际的区别在于,CHECK 仅在为其声明的表更新时进行测试,而ASSERTION 则在不考虑更新中涉及的表的情况下进行测试。
    • “他们不[实施涉及多行的约束的原因是]他们只是不知道该怎么做”——我宁愿认为事实是他们不知道如何使用他们的传统技术有效地做到这一点。例如,他们可以将表级检查约束映射到触发器,但是他们让用户编写触发器,因此无法对由此产生的低劣性能负责。或者他们可以使用新算法从头开始重写他们的产品;)
    • “高效”当然是前提条件。如果“高效”不是要求的一部分,即使我的猫也知道“怎么做”:-)
    • “有趣的是,你、我、作者……都提供了这个 SO 问题的答案!”你可以打赌会有更多这样的。 (如果 Stackoverflow 支持 Relational Division,我们可以很容易地搜索到它们!!!!!!!)Relationland 不是一个很大的游泳池,没有太多的游泳者......(而且大卫肯定是更受尊敬的人之一。)
    • 我对你进一步的 cmets re 很感兴趣。检查约束。标准中似乎确实没有任何内容表明 CHECK 是针对单个行级别的。奇怪的是,似乎也没有任何明确说明 CHECK 是针对 TABLE 级别的!!!!!!!最可以肯定的是,有时在 CHECK 约束表达式的上下文中可用的构造(例如 Oracle 的 :new.)肯定是针对行级别的,并且肯定与针对表级别的完全不兼容)。 ...
    【解决方案3】:

    断言不修改数据,它们只检查某些条件

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


    断言未链接到数据库中的特定表,也未链接到特定事件

    触发器链接到特定表格和特定事件

    【讨论】:

    • 谢谢。 “未链接到特定表的评估”和“链接到特定表的触发器”是什么意思?
    【解决方案4】:

    数据库约束涉及更新数据库时必须满足的条件。在 SQL 中,如果约束条件的计算结果为 false,则更新失败,数据保持不变并且 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 的约束进行测试。

    ASSERTIONCHECK 都可以延迟(如果声明为 DEFERRABLE),允许数据暂时违反约束条件,但仅限于事务内。

    ASSERTIONCHECK 涉及子查询的约束是核心标准 SQL 之外的功能,并且没有一个主要的 SQL 产品支持这些功能。 MS Access(不完全是工业级产品)支持涉及子查询的CHECK 约束,但不支持可延迟的约束,并且总是逐行执行约束测试,实际后果是功能非常有限。

    CHECK 约束一样,触发器应用于特定表。因此,触发器可用于实现与CHECK 约束相同的逻辑,但不能用于ASSERTION。触发器是过程代码,与约束不同,用户必须对性能和错误处理等问题承担更多责任。大多数商业 SQL 产品都支持触发器(前面提到的 MS Access 不支持)。

    【讨论】:

    • 一个 CHECK 应用于特定的基表,而一个 CHECK 约束应用于整个数据库,我认为其中一个 CHECK 必须替换为 Assertion。
    【解决方案5】:

    表达式应该为真才能触发触发器,但只要表达式为假,就会评估检查。

    【讨论】:

      猜你喜欢
      • 2011-09-23
      • 2019-08-09
      • 2013-01-18
      • 2023-02-09
      • 2011-01-11
      • 2010-09-22
      • 2015-03-15
      • 2013-11-04
      • 2022-08-20
      相关资源
      最近更新 更多