关于数据库设计:何时/为什么在SQL Server中使用级联?

关于数据库设计:何时/为什么在SQL Server中使用级联?

When/Why to use Cascading in SQL Server?

在SQL Server中设置外键时,在什么情况下应在删除或更新时级联它,其背后的原因是什么?

这可能也适用于其他数据库。

我正在寻找每种情况的具体示例,最好是从成功使用它们的人那里获得。


到目前为止我所看到的摘要:

  • 有些人根本不喜欢级联。

级联删除

  • 当关系的语义可能涉及排他性的"是"描述的一部分时,级联删除可能很有意义。例如,OrderLine记录是其父订单的一部分,并且OrderLines永远不会在多个订单之间共享。如果Order消失,OrderLine也应该消失,没有Order的行将成为问题。
  • 级联删除的典型示例是SomeObject和SomeObjectItems,在这种情况下,没有相应的主记录就不会存在项目记录。
  • 如果要保留历史记录或仅将已删除的位列设置为1 / true的"软/逻辑删除",则不应使用Cascade Delete。

级联更新

  • 当您在表之间使用实键而不是代理键(标识/自动增量列)时,级联更新可能会很有意义。
  • 当您具有可变的外键(例如可以更改的用户名)时,Cascade Update的典型示例。
  • 您不应将Cascade Update与"身份/自动增量"列的键一起使用。
  • 级联更新最好与唯一约束结合使用。

何时使用级联

  • 您可能希望在允许操作进行级联之前从用户那里获得额外的确认,但这取决于您的应用程序。
  • 如果您错误地设置了外键,级联会给您带来麻烦。但是,如果您这样做正确的话,您应该会没事的。
  • 在完全理解之前,使用级联是不明智的。但是,它是一个有用的功能,因此值得花一些时间来理解。

外键是确保数据库引用完整性的最佳方法。避免由于魔术而导致的级联就像在汇编中编写所有内容一样,因为您不相信编译器背后的魔术。

不好的是错误使用了外键,例如,向后创建外键。

胡安·曼努埃尔(Juan Manuel)的例子就是规范的例子,如果您使用代码,则有更多的机会在数据库中留下伪造的DocumentItem,这些东西会咬住您。

级联更新很有用,例如,当您通过可以更改的内容引用数据时,例如说用户表的主键是名称,姓氏组合。然后,您希望该组合中的更改传播到引用的任何位置。

@Aidan,您所指的这种清晰度付出了高昂的代价,即有可能在数据库中保留虚假数据,这的确不少。对我而言,通常只是缺乏对DB的熟悉,并且在与DB合作之前无法找到哪些FK会加剧这种恐惧。要么,要么是不断地滥用级联,在实体在概念上不相关的地方,或者必须保留历史的地方使用级联。


我从不使用级联删除。

如果我想从数据库中删除某些内容,我想明确地告诉数据库我要取出的内容。

当然,它们是数据库中可用的功能,有时可能可以使用它们,例如,如果您有一个" order"表和一个" orderItem"表,则在删除某个表时可能要清除这些项。订购。

我喜欢从代码(或存储过程)中获得的清晰度,而不是发生"魔术"事件。

出于同样的原因,我也不喜欢触发器。

需要注意的是,即使您删除了一个"订单",即使级联删除删除了50个" orderItem",您也会得到" 1行受影响"的报告。


我经常进行级联删除。

知道有人对数据库进行操作可能永远都不会留下任何不需要的数据,这真是一件好事。如果依赖性增加,我只需在Management Studio中更改图表中的约束即可,而不必调整sp或dataacces。

也就是说,我有1个有关级联删除和循环引用的问题。这通常会导致数据库中没有级联删除的部分。


我做了很多数据库工作,很少发现级联删除有用。我曾经有效地使用过它们的一次是在报表数据库中,该数据库由每夜的工作进行更新。我通过删除自上次导入以来已更改的所有顶级记录,然后重新导入已修改的记录以及与它们相关的任何内容,来确保正确导入任何已更改的数据。它使我不必编写从数据库底部到顶部的许多复杂删除操作。

我不认为级联删除像触发器一样糟糕,因为它们仅删除数据,触发器内部可能包含各种讨厌的东西。

通常,我完全避免使用真正的Delete方法,而是使用逻辑删除方法(即,将名为isDeleted的位列设置为true)。


一个示例是当您在实体之间具有依赖关系时,即:文档-> DocumentItems(删除Document时,DocumentItems没有理由存在)


开删除级联:

想要删除子表中的行时如果在父表中删除了相应的行。

如果不使用级联删除,则将导致引用完整性错误。

在更新级联上:

当您想更改主键中的外键中的更新


如果希望删除带有FK的记录,请使用层叠删除。换句话说,在没有引用记录的情况下记录是没有意义的。

我发现级联删除对确保默认情况下删除无效引用而不是导致空异常有用。


我听说过DBA和/或"公司策略"完全是由于过去的不良经验而禁止使用" On Delete Cascade"(及其他)。在一个案例中,一个人写了三个触发器,最终互相呼唤。三天的恢复导致完全禁止使用触发器,所有这些都是由于一个同志的行为。

当然,有时需要触发器,而不是"删除时级联",例如需要保留一些子数据时。但是在其他情况下,使用"删除时"级联方法完全有效。"删除级联"的主要优点是它可以捕获所有子级。如果编写的自定义触发器/存储过程编码不正确,则可能不会。

我认为应该允许开发人员根据开发内容和规格说明做出决定。基于不良经历的地毯禁令不应作为标准;"永不使用"的思维过程充其量是严酷的。每次都需要进行判断,并且随着业务模型的变化而进行更改。

这不是发展的全部内容吗?


进行级联删除(而不是在代码中进行删除)的一个原因是提高性能。

情况1:使用级联删除

1
 DELETE FROM table WHERE SomeDate < 7 years ago;

情况2:没有级联删除

1
2
3
4
5
 FOR EACH R IN (SELECT FROM table WHERE SomeDate < 7 years ago) LOOP
   DELETE FROM ChildTable WHERE tableId = R.tableId;
   DELETE FROM table WHERE tableId = R.tableid;
   /* More child tables here */
 NEXT

其次,当您使用级联删除添加额外的子表时,情况1中的代码将继续工作。

我只会在关系的语义是"一部分"的情况下放一个层叠。否则,某些白痴在执行以下操作时将删除数据库的一半:

1
DELETE FROM CURRENCY WHERE CurrencyCode = 'USD'

我和这里的其他人一样,发现级联删除实际上仅在一定程度上有所帮助(删除其他表中的引用数据确实没有太多工作-如果有很多表,您只需使用脚本即可自动执行此操作)但确实很烦人当某人意外地级联删除一些难以恢复的重要数据时。

我唯一使用的情况是表格表中的数据受到高度控制(例如,有限的权限),并且仅通过已验证的受控过程(例如软件更新)进行更新或删除。


我尝试避免删除或更新我未在SQL Server中明确请求的内容。

通过级联或通过使用触发器。在尝试跟踪错误或诊断性能问题时,它们往往会在一段时间内咬住您。

我用它们的目的是为了保证一致性,而无需付出太多努力。为了获得相同的效果,您将必须使用存储过程。


可以通过以下三种方式之一来处理对S的删除或更新,以删除在R的某些元组中找到的外键值:

  • 拒绝
  • 传播
  • 无效。
  • 传播称为级联。

    有两种情况:

    ?如果删除了S中的元组,请删除引用它的R元组。

    ?如果更新了S中的元组,请更新引用它的R元组中的值。


    当在物理数据库中实现逻辑超类型和子类型实体时,级联删除非常有用。

    当使用单独的超类型和子类型表来物理实现超类型/子类型时(与将所有子类型属性汇总到一个物理超类型表中相反),这些表和问题之间的一种关系就变成了如何使这些表之间的主键100%保持同步。

    级联删除对于以下操作而言可能是非常有用的工具:

    1)确保删除超类型记录也删除了相应的单个子类型记录。

    2)确保对子类型记录的任何删除也会删除超类型记录。这是通过在子类型表上实现"代替"删除触发器来实现的,该触发器触发并删除相应的超类型记录,然后,该超类型记录又级联删除子类型记录。

    以这种方式使用级联删除可确保不存在任何孤立的超类型记录或子类型记录,而不管您是先删除超类型记录还是先删除子类型记录。


    如果您正在使用具有不同版本的许多不同模块的系统,则如果级联删除的项目是PK持有者的一部分/拥有的,这将非常有帮助。否则,所有模块在删除PK所有者之前都需要立即打补丁以清理其依赖项,否则,如果没有正确执行清理操作,则将完全省略外键关系,这可能会在系统中留下大量垃圾。

    在不鼓励层叠删除一段时间后,我刚刚为两个已经存在的表之间的新交集表(仅要删除的交集)引入了级联删除。如果数据丢失,也还不错。

    但是,这对枚举式列表表来说是一件坏事:有人从表" colors"中删除条目13-黄色,并且数据库中的所有黄色项都将被删除。而且,有时这些内容会以全部删除全部插入的方式进行更新,从而导致完全省略参照完整性。当然,这是错误的,但是您将如何更改已经运行了多年的复杂软件,而引入真正的参照完整性却会带来意料之外的副作用?

    另一个问题是,即使删除了主键,仍应保留原始外键值。可以为原始FK创建一个逻辑删除列和一个ON DELETE SET NULL选项,但这又需要触发器或特定的代码来维护冗余(PK删除后除外)键值。


    推荐阅读