【发布时间】:2018-08-06 17:51:48
【问题描述】:
如何对表设置约束以强制每组只有一条记录被标记为“主要”。
例如在一家提供冒险刺激运动的公司的数据库中,他们有表 Customer 和表 EmergencyContact,其中有从 EmergencyContact 到 Customer 的 FK 约束。每个客户记录可能有零个或多个 EmergencyContact 记录。如果客户有一个或多个紧急联系人,则其中一个必须标记为主要联系人。如果只有一个紧急联系人,那么显然必须将其标记为主要联系人。
请注意,相关约束很容易实施,并且可能是解决方案的一部分 - 强制每个客户不超过一个紧急联系人被标记为主要联系人。 stackoverflow 上已经有很多这样的问题(和好的答案),例如here、here 等。但是,我还没有找到答案(或任何其他方式)来强制每个组至少有一个子记录被标记,其中存在任何子记录。
如果可能,我想避免在计算列中使用触发器或基于 UDF 的约束。触发器可能会被禁用,并且并不总是触发(参见 Alex Kuznetsov 的优秀书籍的第 208 页Defensive Database Programming),并且包含在检查约束中的 UDF 在某些情况下也可能被绕过或忽略(参见同一本书的第 184 页) )。
我尝试了几种方法来解决这个问题,例如索引视图以及从父表到子表的 FK 约束,但尚未成功。
【问题讨论】:
-
请注意,只有具有 ALTER TABLE 权限的用户才能绕过触发器执行,并且此类用户还可以禁用外键和检查约束,重新启用它们检查现有行,并禁用索引。
-
@DavidBrowne-Microsoft - 使用快照事务隔离级别时,强制约束(如此处)的触发器也可能失败,除非采取特殊预防措施。 (即便如此,我也不确定它们是否 100% 可靠)。
-
完全同意。编写始终按照您的意图执行并且不会引入性能问题或死锁的触发器是很棘手的。以至于放弃让数据库强制执行特定的业务规则通常比使用触发器强制执行要好。
-
@DavidBrowne-Microsoft 触发器有时会在快照隔离下运行时失败。请参阅 Alex Kuznetsov 的优秀书籍 Defensive Database Programming with SQL Server,从第 114 页开始,尤其是第 122 页的 SNAPSHOT 模式下的触发行为副标题。(alternative download link)
-
引自使用 SQL Server 进行防御性数据库编程 的话:“我们能否开发出更强大的触发器;在快照隔离下继续执行我们的业务规则的触发器?在这种情况下,我不这么认为。"