何时/为什么在SQL Server中使用级联?
在SQL Server中设置外键时,在什么情况下应该将其删除或更新级联,其背后的原因是什么?
这可能也适用于其他数据库。
我最重要的是看每个场景的具体例子,最好是成功使用过的人。
我到目前为止所看到的总结:
- 有些人根本不喜欢层叠。
级联删除
- 级联删除可能是有意义的,当关系的语义可以涉及排他的“是 ”的描述的一部分 。 例如,OrderLinelogging是其父订单的一部分,OrderLines将永远不会在多个订单之间共享。 如果订单消失,订单行也应该是这样的,没有订单的行将是一个问题。
- Cascade Delete的典型例子是SomeObject和SomeObjectItems,在没有对应的主logging的情况下,项目logging永远不存在。
- 如果要保留历史logging或使用“软/逻辑删除”,则只应将删除位列设置为1 / true,否则不应使用级联删除。
级联更新
- 在跨表使用真实密钥而不是代理键(标识/自动增量列)时,级联更新可能有意义。
- Cascade Update的典型例子是当你有一个可变的外键,像一个可以改变的用户名。
- 您不应使用级联更新和身份/自动增量列的键。
- 级联更新最好与一个唯一约束一起使用。
何时使用级联
- 在允许操作级联之前,您可能希望得到来自用户的强烈确认,但这取决于您的应用程序。
- 级联可能会让你陷入麻烦,如果你设置你的外键错误。 但是,如果你做得对,你应该没问题。
- 在彻底理解之前使用级联是不明智的。 但是,这是一个有用的function,因此值得花时间去理解。
外键是确保数据库参照完整性的最佳方法。 因为变魔术而避免级联就像编写所有的程序集,因为你不相信编译器背后的魔力。
例如,错误的使用外键是不好的,例如反向创build它们。
Juan Manuel的例子就是典型的例子,如果你使用代码,那么在数据库中留下假的DocumentItems的机会还有很多,
级联更新是有用的,例如,当你通过可以改变的东西来引用数据时,例如用户表的主键是名字,姓氏组合。 然后,您需要将该组合中的更改传播到所引用的任何位置。
@Aidan,你提到的那种清晰度是以高昂的代价,在数据库中留下虚假数据的机会,这个数字并不小 。 对我来说,通常只是不熟悉数据库,也不能在与数据库一起工作之前find哪些FKs。 要么是,要么是不断的滥用级联,要么在实体没有概念关系的地方使用它,要么保留历史。
我从不使用级联删除。
如果我想从数据库中删除一些东西,我想明确地告诉数据库我想要的东西。
当然,它们是数据库中可用的function,有时可以使用它们,例如,如果您有“订单”表和“订单项”表,则可能需要在删除订购。
我喜欢从代码(或存储过程)中获得的清晰度,而不是“魔术”的发生。
出于同样的原因,我也不是触发器的粉丝。
需要注意的是,如果删除'订单',即使级联删除删除了50个'orderItem',您也会收到'1 row affected'报告。
我通过级联删除工作了很多。
知道谁对数据库工作可能永远不会留下任何不需要的数据,这感觉很好。 如果依赖关系增长,我只是在Management Studio中更改图表中的约束条件,而不必调整sp或dataacces。
这就是说,我有一个级联删除问题,那就是循环引用。 这通常导致部分数据库没有级联删除。
一个例子是当你有实体之间的依赖关系… ie:Document – > DocumentItems(当你删除Document时,DocumentItems没有理由存在)
我做了很多数据库工作,很less发现级联删除有用。 有一次,我有效地使用它们是在夜间工作更新的报告数据库中。 通过删除自上次导入以来更改过的所有顶级logging,确保所有已更改的数据都能正确导入,然后重新导入已修改的logging以及任何与之相关的logging。 它使我不必从写入数据库的底部到顶部写入大量复杂的删除操作。
我不认为级联删除和触发器一样坏,因为它们只是删除数据,触发器里面可能有各种讨厌的东西。
一般来说,我完全避免真正的删除,而是使用逻辑删除(即有一个名为isDeleted的位列被设置为true)。
如果删除引用的PKlogging,则使用级联删除,如果您希望FK的logging被删除。 换句话说,没有参考logging的logging是没有意义的。
我发现级联删除有用,以确保默认情况下删除死引用,而不是导致空例外。
放入级联删除(而不是在代码中执行)的一个原因是提高性能。
情况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'
ON删除级联:
当您希望子表中的行被删除时如果在父表中删除 相应的行 。
如果没有使用级联删除,那么参考完整性将会出现错误。
ON更新级联:
当你想在主键更改 外键更新
我尽量避免删除或更新,我没有在SQL服务器中明确要求。
通过级联或通过使用触发器。 无论是在追踪一个bug还是在诊断性能问题的时候,他们往往会在屁股上咬你一顿。
我会在哪里使用它们是为了保证一致性而不是很费力。 要获得相同的效果,你将不得不使用存储过程。
我和其他人一样,发现级联删除实际上只是非常有帮助的(删除其他表中的引用数据真的没有太多的工作 – 如果有很多表,只需用脚本自动化),但真的很烦人当有人意外地级联删除了一些难以恢复的重要数据。
我使用的唯一情况是如果表格中的数据受到高度控制(例如,有限的权限),并且只能通过已validation的受控进程(如软件更新)更新或删除。
过去我曾经有过DBA和/或“公司政策”禁止使用“On Delete Cascade”(和其他人),纯粹是因为过去不好的经历(曾经有人写过三个触发器,最后互相打电话 – 3天后恢复,导致全面禁止触发 – 因为一个idjit的行动)。
当然,在某些情况下,当需要保留一些子数据时,需要使用触发器而不是“在删除级联”,但在其他情况下,使用On Delete级联方法是完全有效的。
开发人员应该能够根据开发的内容以及规范说明来做出决定。 基于不良体验的地毯禁令不应该成为标准。 在删除级联将捕获所有的孩子,一个自定义的书面触发/存储过程可能不会如果没有正确编码。
“永不使用”的思维过程充其量就是严酷的。 每次都需要进行判断调用,并根据业务模型的变化进行更改。
这不是什么“发展”是关于?
删除或更新S以移除在R的某些元组中find的外键值可以通过以下三种方式之一来处理:
- 拒绝
- 传播
- 无效。
传播被称为级联。
有两种情况:
‣如果S中的元组被删除,删除引用它的R元组。
‣如果S中的元组已更新,请更新引用它的R元组中的值。
如果您正在使用不同版本的多个不同模块的系统,如果级联删除项目是PK持有者的一部分/拥有,这可能会非常有帮助。 否则,所有模块在删除PK所有者之前都需要立即修补它们的相关项目,否则外键关系将被完全省略,如果清理不正确,可能会在系统中留下大量的垃圾。
我刚才介绍了两个已经存在的表(仅删除交集)之间的一个新交集表的级联删除,级联删除之后已经有相当一段时间了。 如果数据丢失也不算太坏。
然而,对枚举类列表表是一件坏事:有人从表“颜色”中删除条目13-黄色,数据库中的所有黄色条目都被删除。 而且,这些有时会以全部删除的方式进行更新,从而导致完全忽略参照完整性。 当然这是错误的,但是如何改变已经运行了多年的复杂软件,引入真正的参考完整性,并且有可能出现意想不到的副作用?
另一个问题是,甚至在主键被删除之后,原始的外键值也应该保持。 可以为原始FK创build逻辑删除列和ON DELETE SET NULL选项,但是这又需要触发器或特定代码来维护冗余(PK删除后除外)键值。
在物理数据库中实现逻辑超types和子types实体时,级联删除非常有用。
当使用单独的超types和子types表来物理地实现超types/子types(而不是将所有子types属性卷成单个物理超types表)时,存在一对一这些表之间的一个关系和问题就变成了如何保持这些表之间100%的主键同步。
级联删除可以是一个非常有用的工具:
1)确保删除超级types的logging也会删除相应的单个子types的logging。
2)确保任何子typeslogging的删除也删除超级types的logging。 这是通过在子types表上执行“取代”删除触发器来实现的,删除相应的超级typeslogging,然后级联删除子typeslogging。
以这种方式使用级联删除可确保不存在孤立的超级types或子typeslogging,而不pipe是先删除超级typeslogging还是先删除子typeslogging。