避免循环/多个更新路径的首选设计

Han*_*non 5 database-design sql-server referential-integrity

考虑以下设计:

CREATE TABLE dbo.Farmers
(
    FarmerName varchar(10) NOT NULL
        PRIMARY KEY 
);

CREATE TABLE dbo.FarmEquipment
(
    FarmEquipmentName varchar(10) NOT NULL
        PRIMARY KEY 
    , CreatorFarmer varchar(10) NOT NULL
        FOREIGN KEY REFERENCES dbo.Farmers(FarmerName)
        ON UPDATE CASCADE
        ON DELETE CASCADE
);

CREATE TABLE dbo.Fields
(
    FieldName varchar(10) NOT NULL
    , OwnerFarmer varchar(10) NOT NULL
        FOREIGN KEY REFERENCES dbo.Farmers(FarmerName)
        ON UPDATE CASCADE
        ON DELETE CASCADE
    , FarmEquipment varchar(10) NOT NULL
        FOREIGN KEY REFERENCES dbo.FarmEquipment(FarmEquipmentName)
        ON UPDATE CASCADE
        ON DELETE CASCADE
);
Run Code Online (Sandbox Code Playgroud)

用例:

  • 农民乔拥有土地。
  • 农民泰德拥有土地。
  • Farmer Joe 建造了一台联合收割机,并将其借给 Farmer Ted 用于 Farmer Ted 的一块田地。
  • 如果 Farmer Joe 将他的名字改为 Farmer Joseph,我希望这能反映在Fields表格中,这样 Farmer Ted 就知道谁拥有他一直在使用的联合收割机。

我意识到这是代理键的确切用例,但我试图确定如果您使用自然键,这将如何工作。

在 SQL Server 中,您实际上无法创建此结构,因为中的ON UPDATE CASCADEandON UPDATE DELETE子句会dbo.Fields产生以下错误消息:

消息 1785,级别 16,状态 0,第 21
行在表“字段”上引入 FOREIGN KEY 约束“FK__Fields__FarmEqui__3AD6B8E2”可能会导致循环或多个级联路径。
指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。

消息 1750,级别 16,状态 0,第 21 行
无法创建约束。请参阅以前的错误。

这个模型在 SQL Server 中应该如何表达?

我意识到我可以删除ON UPDATE条款,但这不是重点。我希望更新和删除级联。

RDF*_*ozz 3

我见过的解决方案是删除外键约束,并通过触发器维护关系完整性。

在外部(或“子”)表上,您需要INSERTUPDATE触发器。如果插入/更新“外键”列,则必须确保该值存在于“父”表的主键中。

在“父”表的UPDATE主键列上,您必须使用旧值找到任何“子”表(FarmEquipment并且Fields都是“子” Farmers)中的所有行,并将它们更新为新值价值。

在“父”表上,DELETE您必须检查“子”表以查看是否有任何行使用有问题的主键,并将它们全部删除。

在使用过以这种方式工作的供应商提供的系统后,我不能推荐它。

  • 如上所述,您必须为每个外键关系维护三块代码,至少跨两个触发器(至少暂时不讨论自引用情况)。
  • 绑定到多个“子”表的“父”表需要检查每个“子”表。虽然每个“子”表的代码应该非常相似,但这实际上会使修改变得更加困难,因为很容易做出旨在错误影响的FarmEquipment更改Fields。(在我提到的实际示例中,至少有一个父表具有至少 20 个“FK”关系 - 有时在同一个子表中具有指向不同字段的多个链接。
  • 关系的文档不再是内置的。虽然您无法(据我所知)右键单击一个表并查找指向该表的所有外键关系,但很容易找到一个查询来从 SQL Server 的结构表中检索此信息。当通过触发器维护关系时,您几乎必须手动检查触发器来识别所有关系以及精确的定义(“父”表的触发器是您DELETE可以确定关系是否应该是CASCADE,,,,或者- 使用外键约束,这可以从“子”表中看到)。NO ACTIONSET NULLSET DEFAULT

    有一些解决方法(触发器代码确实以可查询的形式存在),但它们要求开发人员遵循有关命名约定、查询构造、甚至可能是代码格式的严格规则。即使一个人正在维护代码,并使用模板来设置所有内容,保持所有内容准确无误,以便在进行更改、发现错误并修复时变得更加困难。

我使用的系统通过这种方法已经运行了 10 多年。他们在没有有效外键的环境中启动了他们的应用程序。但即使他们已经到了对新表使用外键的地步,并开始尽可能将它们放在现有表上。只是使用它并不容易。

我怀疑这不符合您的标准;然而,由于它一个可行的解决方案,我认为它至少值得讨论。