如何在亚型中实施参照完整性

我在关系数据库中有以下表格:

[Sensor] LocationId [PK / FK -> Location] SensorNo [PK] [AnalogSensor] LocationId [PK/FK -> Sensor] SensorNo [PK/FK -> Sensor] UpperLimit LowerLimit [SwitchSensor] LocationId [PK/FK -> Sensor] SensorNo [PK/FK -> Sensor] OnTimeLimit [Reading] LocationId [PK/FK -> Sensor] SensorNo [PK/FK -> Sensor] ReadingDtm [PK] [ReadingSwitch] LocationId [PK/FK -> Reading] SensorNo [PK/FK -> Reading] ReadingDtm [PK/FK -> Reading] Switch [ReadingValue] LocationId [PK/FK -> Reading] SensorNo [PK/FK -> Reading] ReadingDtm [PK/FK -> Reading] Value [Alert] LocationId [PK/FK -> Reading] SensorNo [PK/FK -> Reading] ReadingDtm [PK/FK -> Reading] 

基本上,ReadingSwitch和ReadingValue是Reading的子types,SwitchSensor和AnalogSensor是Sensor的子types。 一个读数可以是一个SwitchReading或ValueReading值 – 它不能同时为一个,而一个传感器可以是一个AnalogSensor或一个SwitchSensor。

我到目前为止所做的唯一途径就是在这里 。

肯定有更好的方法来做这种事情。

我能想到的唯一的另一种方式是不具有子types,而是完全扩展一切:

 [SwitchSensor] LocationId [PK/FK -> Location] SensorNo [PK] [AnalogSensor] LocationId [PK/FK -> Location] SensorNo [PK] [SwitchReading] LocationId [PK/FK -> SwitchSensor] SensorNo [PK/FK -> SwitchSensor] ReadingDtm Switch [AnalogReading] LocationId [PK/FK -> AnalogSensor] SensorNo [PK/FK -> AnalogSensor] ReadingDtm Value [AnalogReadingAlert] LocationId [PK/FK -> AnalogReading] SensorNo [PK/FK -> AnalogReading] ReadingDtm [PK/FK -> AnalogReading] [SwitchReadingAlert] LocationId [PK/FK -> SwitchReading] SensorNo [PK/FK -> SwitchReading] ReadingDtm [PK/FK -> SwitchReading] 

这可能不是那么糟糕,但我也有引用Alert表的表,所以它们也必须被复制:

 [AnalogReadingAlertAcknowledgement] ... [AnalogReadingAlertAction] ... [SwitchReadingAlartAcknowledgement] ... [SwitchReadingAlartAction] 

等等

这个问题对任何人都有意义吗?

没有必要,特别是不把表格翻倍。

介绍

由于关系型数据库build模标准(IDEF1X)已经被使用了25年以上(至less在高质量,高性能的市场),我使用这个术语。 Darwen, 尽pipe他已经做了很大的努力来取得进展 1 ,但是他仍然没有意识到IDEF1X,直到2009年我提到他的时候,并且因此有了新的术语2几十年来我们一直使用的标准术语。 而且,新的术语并不涉及所有的情况,正如IDEF1X所做的那样。 因此,我使用既定的标准术语,并避免使用新的术语。

  • 即使“分布式密钥”的概念也没有认识到基本的普通PK :: FK关系,它们在SQL中的实现以及它们的能力。

  • 关系,因此IDEF1X的概念是标识符和迁移。

  • 当然,这些供应商并不是完全处在这个舞台上,而且他们有怪异的东西,比如说“部分指数”等等,在理解这些基本知识时,这是完全不必要的。 但是在25年前,当概念被标准化并且得到了充分的处理的时候,有名的学者提出了不完整的新概念……这是意想不到的。

警告

IEC / ISO / ANSI SQL几乎不能完全处理5NF,它根本不支持超types – 子types结构; 没有声明约束(这应该是)。

  • 因此,为了强制执行数据模型中规定的整套规则,SuperType :: Subtype和Subtype :: Supertype,我们必须用CHECK Constraints等操作(我避免使用Triggers有很多原因) 。

浮雕

不过,我考虑到了这一切。 为了让我有效地在StackOverflow上提供一个数据build模服务,而不必以一个完整的话语开头,我故意提供模型,可以由有能力的人使用现有的SQL和现有的约束来实现,无论他们需要什么程度。 它已经被简化了,并且包含了执行的一般水平。 如果有任何问题,只要问,你就会收到。

我们可以在链接文档中使用示例graphics,并使用完全符合IDEF1X的▶传感器数据模型◀

不熟悉关系build模标准的读者可能会发现▶IDEF1X Notation◀有用。 如果读者认为数据库可以映射到对象,类和子类,则build议进一步阅读可能会导致受伤。 这比福勒和安布勒读过的还要进一步。

超types的参照完整性实现

有两种types的超types – 子types结构。

独占子types

独占意味着每个超types行只能有一个子types行。 在IDEF1X术语中,Supertype中应该有一个Discriminator列,用于标识Supertype行和哪个Subtype行存在。

  • 对于两个以上的子types,这是要求的,我实现了一个Discriminator列。

  • 对于两个子types,由于这很容易从现有的数据中派生出来(例如Sensor.IsSwitchReading的判别器),所以我没有为Reading进行额外的明确的Discriminator列build模。 但是,你可以自由地遵循标准来执行判决。

我将详细介绍每个方面。

  1. Discriminator列需要一个CHECK约束来确保它在值的范围内,例如: IN ("B", "C", "D")IsSwitch是一个BIT,它是0或1,所以已经被约束了。

  2. 由于超types的PK定义了它的唯一性,所以只允许有一个超types行; 没有第二个超types行(因此没有第二个子types行)可以被插入。

    • 因此,如您的链接所build议的那样,在超types中实施诸如(PK,鉴别器)的索引是过度的,完全多余的,额外的不必要的索引。 PK的独特之处,因此PK加上任何东西都是唯一的)。

    • IDEF1X在Subtype表中不需要Discriminator。 在子types中,再次受到其PK的唯一性限制的子types,如果鉴别器被实现为该表中的列,则其中的每一行将具有与鉴别器相同的值(每本书将是“ B“;每个IsSwitch将是一个IsSwitch )。 因此,在Subtype中实施Discriminator作为一个列是荒谬的。 再次,完全多余的,一个额外的不必要的索引,以实现一个索引,如(PK,鉴别器)在亚型:唯一性在PK,因此PK加上任何东西将是唯一的)。

    • 链接中标识的方法是实施参照完整性的一个障碍和臃肿(无用的大规模数据复制)方式。 作者还没有看到在其他地方构build的很好的理由。 理解SQL并以有效的方式使用它是一个基本的失败。 这些“解决scheme”是遵循“SQL不能做……”的教条的典型人物,因此对于SQL可以做什么是盲目的。 福勒和安布勒盲目的“方法”造成的恐怖更加严重。

  3. PK子types也是超types的FK,即所有必需的,以确保子types不存在没有父超types。

    • 因此,对于任何给定的PK,无论哪个超types子类先插入将成功; 并且在那之后尝试任何Supertype-Subtype,都将失败。 因此,在子types表中没有什么可担心的(对于相同的PK,第二超types行或第二子types行被阻止)。
  4. SQL CHECK Constraint仅限于检查插入的行 。 我们需要检查插入的行对其他行 ,无论是在同一个表中,或在另一个表中。 因此需要用户定义的function。

    • 编写一个简单的UDF,检查PK SuperType中的鉴别器是否存在,如果EXISTS返回1,否则返回0。 您将需要一个超types的UDF(不是每个子types)。

    • 在子types中,使用PK(既是超types又是子types)和判别器实现调用UDF的CHECK约束。

    • 我已经在不同的SQL平台上用数十个大型的现实世界的数据库来实现这个function。 这是用户定义的function代码◀ ,以及它所基于的对象的DDL代码◀

    • 这个特定的语法和代码在Sybase ASE 15.0.2上进行了testing(它们对SQL标准的合规性非常保守)。

    • 我知道用户定义函数的限制对于每个SQL平台都是不同的。 不过,这是最简单的,AFAIK每个平台都允许这个构造。 (不知道非SQL的做什么。)

    • 是的,当然这个聪明的小技巧可以用来实现任何可以在数据模型中绘制的非平凡的数据规则。 特别要克服SQL的局限性。 请注意避免双向约束(循环引用)的警告。

  5. 因此,子types中的CHECK约束确保了超types中存在PK加正确的判别符。 这意味着只有超types(PK)的子types存在。

    • 任何后续尝试插入另一个子types(即打破专有规则)将失败,因为PK +鉴别器不存在超types。

    • 任何子序列尝试插入同一个子types的另一行都会被其PK约束的唯一性所阻止。

  6. 唯一缺less的(链接中没有提到的)是规则“每个超类必须至less有一个子types”没有被强制执行。 这很容易覆盖在事务代码(我build议限制在两个方向,或触发器); 使用正确的工具来完成这项工作。

非独占子types

超types(父)可以承载多个子types(子)

  1. 没有一个子types被识别。

    • 鉴别者不适用于非排他子types。

    • 子types的存在通过使用超typesPK对子types表进行存在检查来识别。

  2. 只需排除调用上述UDF的CHECK约束。

    • PRIMARY KEY,FOREIGN KEY和通常的范围检查约束条件充分支持非排他子types的所有要求。

参考

欲了解更多详情, 包括细节的图表概述; 以及子types和可选列表之间的区别,请参阅此子types文档

注意

  1. 我也被CJ Date和Hugh Darwen不断提及的“推进” 关系模型所接纳。 基于一致的证据,经过多年的互动,我得出结论,他们的工作实际上是贬低它的。 他们没有采取任何行动来进一步提高EF Codd博士的开创性工作,也没有做任何事情来破坏和压制它。

  2. 他们有关系术语的私人定义,这当然会严重阻碍任何沟通。 他们对我们自1970年以来的术语有了新的术语,以performance出他们“发明”了它。 典型的欺诈和盗贼。

第二个选项也充满了问题 – 例如对于传感器(并假设SensorNo是代理键),因为您没有基表,所以SensorNo代理不是在子类表之间唯一的,除非您使用klugey机制在所有子类表中发布密钥(或使用guid)。

如果您的用户界面“结合”了不同types的传感器,例如显示模拟和开关传感器联合的列表,这将会被放大。

我build议你保持你的第一个模式,然后用经过良好testing的事务代码封装这些表的插入和维护。

例如为各种子表格创build插入过程,这些子表格在一个工作单元下插入相应的基类和子类别logging。 您可以进一步撤销对任何表的INSERT权限,从而强制插入通过SPROC。

您也可以运行每日完整性报告,检查是否没有违反您的inheritance结构。