166

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

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

我最关注的是每种场景的具体示例,最好是来自成功使用它们的人。

4

15 回答 15

139

到目前为止我所看到的总结:

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

级联删除

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

级联更新

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

何时使用级联

  • 在允许操作级联之前,您可能希望从用户那里获得额外的强烈确认,但这取决于您的应用程序。
  • 如果您错误地设置了外键,级联可能会给您带来麻烦。但如果你做对了,你应该没问题。
  • 在完全理解之前使用级联是不明智的。但是,它是一个有用的功能,因此值得花时间去理解。
于 2008-09-12T16:43:57.333 回答
73

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

不好的是错误使用外键,例如反向创建它们。

Juan Manuel 的示例是典型示例,如果您使用代码,则更有可能在数据库中留下虚假的 DocumentItems,它们会来咬您。

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

@Aidan,您提到的这种清晰度需要付出高昂的代价,在您的数据库中留下虚假数据的机会并不小。对我来说,通常只是对 DB 不熟悉,并且在与 DB 合作之前无法找到哪些 FK 会导致这种恐惧。要么,要么不断滥用级联,在实体在概念上不相关的地方使用它,或者你必须保留历史。

于 2008-09-12T15:46:54.007 回答
19

我从不使用级联删除。

如果我想从数据库中删除一些东西,我想明确地告诉数据库我想要取出什么。

当然,它们是数据库中可用的功能,有时可以使用它们,例如,如果您有一个“订单”表和一个“订单项”表,您可能希望在删除一个命令。

我喜欢通过代码(或存储过程)而不是“魔术”发生而获得的清晰度。

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

需要注意的是,如果您确实删除了“订单”,即使级联删除已删除 50 个“订单项”,您也会收到“受影响的 1 行”报告。

于 2008-09-12T15:36:12.753 回答
13

我经常使用级联删除。

知道对数据库工作的人可能永远不会留下任何不需要的数据,这感觉很好。如果依赖关系增加,我只需在 Management Studio 中更改图表中的约束,而不必调整 sp 或 dataacces。

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

于 2008-09-16T19:07:50.747 回答
10

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

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

一般来说,我完全避免真正的删除,而是使用逻辑删除(即有一个名为 isDeleted 的位列设置为 true)。

于 2008-09-12T15:51:59.173 回答
9

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

于 2008-09-12T15:33:29.250 回答
5

如果删除了引用 PK 记录,则在您希望删除带有 FK 的记录的地方使用级联删除。换句话说,如果没有引用记录,记录就毫无意义。

我发现级联删除对于确保默认删除死引用而不是导致空异常很有用。

于 2013-03-01T11:19:08.410 回答
5

ON 删除级联:

当您希望删除子表中的行时如果在父表中删除相应的行。

如果未使用on cascade delete,则会为引用完整性引发错误。

ON 更新级联:

当您希望在外键中更新主键的更改时

于 2014-01-12T16:03:45.787 回答
5

我听说过 DBA 和/或“公司政策”纯粹因为过去的糟糕经历而禁止使用“On Delete Cascade”(和其他)。在一个案例中,一个人写了三个触发器,最终相互调用。三天的恢复导致完全禁止触发器,这一切都是因为一个 idjit 的行为。

当然,有时需要触发器而不是“删除级联”,例如需要保留一些子数据时。但在其他情况下,使用 On Delete 级联方法完全有效。“On Delete cascade”的一个关键优势是它捕获了所有的孩子;如果编码不正确,自定义编写的触发器/存储过程可能不会。

我认为应该允许开发人员根据开发内容和规范说明做出决定。基于糟糕体验的地毯禁令不应成为标准;“从不使用”的思维过程充其量是严厉的。每次都需要做出判断,并随着商业模式的变化而做出改变。

这不就是发展的意义吗?

于 2014-06-23T14:29:05.837 回答
3

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

案例 1:使用级联删除

 DELETE FROM table WHERE SomeDate < 7 years ago;

案例 2:没有级联删除

 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 中的代码将继续工作。

我只会在关系的语义是“一部分”的情况下放入级联。否则一些白痴会在你这样做时删除一半的数据库:

DELETE FROM CURRENCY WHERE CurrencyCode = 'USD'
于 2008-10-19T11:31:40.670 回答
2

我尽量避免在 SQL Server 中没有明确请求的删除或更新。

通过级联或使用触发器。他们往往会在一段时间后咬你一口,无论是在试图追踪错误或诊断性能问题时。

我会使用它们的地方是保证一致性,而不是太多的努力。要获得相同的效果,您必须使用存储过程。

于 2008-09-12T15:34:38.420 回答
2

我和这里的其他人一样,发现级联删除实际上只有一点点帮助(删除其他表中的引用数据实际上并没有那么多工作——如果有很多表,你只需使用脚本自动执行此操作)但真的很烦人当有人不小心级联删除了一些难以恢复的重要数据时。

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

于 2008-09-16T19:05:36.210 回答
1

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

  1. 拒绝
  2. 传播
  3. 作废。

传播称为级联。

有两种情况:

‣ 如果 S 中的元组被删除,则删除引用它的 R 元组。

‣ 如果 S 中的元组已更新,则更新引用它的 R 元组中的值。

于 2016-01-25T00:40:48.627 回答
0

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

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

然而,对于类似枚举的列表表来说,这是一件坏事:有人从表“颜色”中删除条目 13 - 黄色,并且数据库中的所有黄色项目都被删除。此外,这些有时会以 delete-all-insert-all 的方式进行更新,导致完全省略参照完整性。当然这是错误的,但是你将如何改变一个已经运行多年的复杂软件,而引入真正的参照完整性可能会带来意想不到的副作用?

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

于 2014-08-05T20:21:04.600 回答
0

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

当单独的超类型和子类型表用于物理实现超类型/子类型(而不是将所有子类型属性汇总到单个物理超类型表中)时,存在一对一-这些表之间的关系,然后问题就变成了如何在这些表之间保持主键 100% 同步。

级联删除可能是一个非常有用的工具:

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

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

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

于 2016-12-09T20:39:33.090 回答