外键约束可能导致循环或多个级联路径?

171 sql sql-server constraints

当我尝试向表格添加约束时,我遇到了问题.我收到错误:

在表'Employee'上引入FOREIGN KEY约束'FK74988DB24B3C886'可能会导致循环或多个级联路径.指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束.

我的约束是在Code表和employee表之间.该Code表包括Id,Name,FriendlyName,TypeValue.在employee具有许多参考代码,使得可以存在对于每种类型的码的参考字段.

如果删除引用的代码,我需要将字段设置为null.

我有什么想法可以做到这一点?

one*_*hen 173

SQL Server对级联路径进行简单计数,而不是试图确定是否存在任何周期,它假设最坏的并拒绝创建引用操作(CASCADE):您可以而且应该仍然创建没有引用操作的约束.如果你不能改变你的设计(或者这样做会损害你的设计)那么你应该考虑使用触发器作为最后的手段.

FWIW解析级联路径是一个复杂的问题.其他SQL产品将简单地忽略该问题并允许您创建循环,在这种情况下,它将是一个竞争,看看哪个将覆盖最后的值,可能是对设计者的无知(例如ACE/Jet这样做).我理解一些SQL产品将尝试解决简单的情况.事实仍然是,SQL Server甚至没有尝试,通过禁止多条路径来播放它是非常安全的,至少它会告诉你.

微软自己建议使用触发器而不是FK约束.

  • 并且在第一次操作完成后触发器也会执行,因此没有竞争进行. (6认同)
  • @armen:因为你的触发器将明确地提供系统无法隐式弄清楚它自己的逻辑,例如,如果有多个路径用于删除引用操作,那么你的触发器代码将定义删除哪些表以及以哪种顺序删除. (5认同)
  • 我仍然无法理解的一件事是,如果可以通过使用触发器来解决此“问题”,那么触发器为什么不会“导致循环或多个级联路径...”呢? (2认同)
  • @dumbledad:我的意思是,只有在约束(可能是组合)无法完成工作时才使用触发器.约束是声明性的,它们的实现是系统的责任.触发器是过程代码,您必须编写(并调试)实现并承受它们的缺点(性能更差等). (2认同)
  • 这样做的问题是,触发器仅在您删除外键约束时才起作用,这意味着您将无法对数据库插入进行引用完整性检查,因此您需要更多触发器来处理该问题。触发解决方案是一个导致退化数据库设计的兔子洞。 (2认同)

小智 89

具有多个级联路径的典型情况是:具有两个细节的主表,例如"Master"和"Detail1"和"Detail2".这两个细节都是级联删除.到目前为止没有问题.但是,如果两个细节与其他一些表(例如"SomeOtherTable")具有一对多的关系,那该怎么办呢?SomeOtherTable具有Detail1ID列和Detail2ID列.

Master { ID, masterfields }

Detail1 { ID, MasterID, detail1fields }

Detail2 { ID, MasterID, detail2fields }

SomeOtherTable {ID, Detail1ID, Detail2ID, someothertablefields }
Run Code Online (Sandbox Code Playgroud)

换句话说:SomeOtherTable中的一些记录与Detail1-records链接,SomeOtherTable中的一些记录与Detail2记录链接.即使保证SomeOtherTable记录永远不属于两个细节,现在也不可能为这两个细节制作SomeOhterTable的记录级联删除,因为从Master到SomeOtherTable有多个级联路径(一个通过Detail1,一个通过Detail2).现在你可能已经理解了这一点.这是一个可能的解决方案:

Master { ID, masterfields }

DetailMain { ID, MasterID }

Detail1 { DetailMainID, detail1fields }

Detail2 { DetailMainID, detail2fields }

SomeOtherTable {ID, DetailMainID, someothertablefields }
Run Code Online (Sandbox Code Playgroud)

所有ID字段都是关键字段和自动递增.关键在于Detail表的DetailMainId字段.这些字段既是关键的,也是参考的约束.现在可以通过仅删除主记录来级联删除所有内容.缺点是对于每个detail1-record和每个detail2记录,还必须有一个DetailMain记录(实际上是先创建它以获得正确且唯一的id).

  • 任何事情都比编写触发器更好。与其他任何事物相比,它们的逻辑不透明并且效率低下。将大表分成较小的表以进行更精细的控制只是更好的规范化数据库的自然结果,其本身并不是什么值得关注的事情。 (3认同)
  • 您的评论对我理解我所面临的问题有很大帮助。谢谢你!我更喜欢关闭其中一个路径的级联删除,然后以其他方式处理其他记录的删除(存储过程;触发器;通过代码等)。但我会记住您的解决方案(分组在一条路径中)以应对同一问题的可能不同应用...... (2认同)

Bil*_*gan 12

我会指出(功能上)在SCHEMA和DATA中循环和/或多个路径之间存在巨大差异.虽然DATA中的循环和可能的多路径肯定会使处理变得复杂并导致性能问题("正确"处理的成本),但架构中这些特性的成本应该接近于零.

由于RDB中的大多数明显周期都出现在层次结构(组织结构图,部分,子部分等)中,因此不幸的是SQL Server假设最糟糕; 即,架构周期==数据周期.实际上,如果您使用RI约束,则无法在数据中实际构建循环!

我怀疑多路径问题是类似的; 即,模式中的多个路径不一定意味着数据中的多个路径,但我对多路径问题的经验较少.

当然,如果SQL Server 确实允许循环,它仍然会受到32的深度限制,但这对大多数情况来说可能已经足够了.(太糟糕了,但这不是数据库设置!)

"而不是删除"触发器也不起作用.第二次访问表时,将忽略触发器.因此,如果您真的想要模拟级联,则必须在存在循环的情况下使用存储过程.但是,替代删除触发器适用于多路径情况.

Celko提出了一种"更好"的方式来表示不引入周期的层次结构,但存在权衡.


Jav*_*ier 7

有一篇文章介绍了如何使用触发器执行多个删除路径.也许这对复杂场景很有用.

http://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with-a-trigger/