【问题标题】:What is the best way to implement Polymorphic Association in SQL Server?在 SQL Server 中实现多态关联的最佳方法是什么?
【发布时间】:2011-08-09 17:28:55
【问题描述】:

我有大量的实例需要在我的数据库中实现某种多态关联。我总是浪费大量时间重新考虑所有选项。这是我能想到的3个。我希望 SQL Server 有一个最佳实践。

这里是多列方法

这里是没有外键的方法

这是基表方法

【问题讨论】:

  • 看这里:stackoverflow.com/questions/2002985/…。它提倡您使用第三种方法,我认为这是最好的,因为它允许在不向关联表添加新(稀疏)列的情况下添加新表,并且具有引用完整性。
  • 方法 1 是维护的噩梦。例如,添加 Object4 将需要全栈更改,因为 Something 表/类/模型/视图都必须更改。方法 2 更好,但任何引用完整性都必须在数据库外部强制执行。最后一种方法是最灵活的,因为您可以(可能)在其之上创建更多通用逻辑和 UI 层,从而减少对架构和使用它的事物的更改。
  • 不是直接的答案,而是需要考虑的问题。如果我实际上不需要非规范化我的值(比如文档或设备配置),我会将对象存储为 SQLXML。这样做可以解决这个问题,并且 SQL 对针对它的查询有很好的原生支持。当用例适合时,它可以为您节省大量精力。这些类型的挑战也促使我想到 MongoDB 等 No-SQL 解决方案。我经常在我的产品中同时使用 SQL 和 MongoDB,因为它们各有千秋。
  • 对于#3,单个对象记录可以同时是对象1、对象2和/或对象3,即多重继承这一事实如何?在某些情况下您可能希望这样做,但如果您不需要并且需要 Object1、Object2 和 Object3 在 ObjectID 上互斥,该怎么办?

标签: sql-server associations polymorphic-associations database-normalization


【解决方案1】:

此模型的另一个常见名称是超类型模型,其中一个具有一组基本属性,可以通过连接到另一个实体来扩展这些属性。在 Oracle 书籍中,它以逻辑模型和物理实现的形式教授。没有关系的模型将允许数据增长到无效状态和孤立记录,我会在选择该模型之前强烈验证需求。具有存储在基础对象中的关系的顶级模型将导致空值,并且在字段互斥的情况下,您将始终为空值。在子对象中强制执行密钥的底部图将消除空值,但也会使依赖关系成为软依赖,如果未强制执行级联,则允许孤儿。我认为评估这些特征将帮助您选择最适合的模型。我过去三个都用过。

【讨论】:

    【解决方案2】:

    我使用以下解决方案来解决类似的问题:

    基于Many-Many的设计:尽管ObjectN和Something之间的关系是1-Many,但它相当于修改了关系表的PK后的Many-Many关系。

    首先,我在每个对象的 ObjectN 和 Something 之间创建一个关系表,然后我使用 Something_ID 列作为 PK。

    这是Something-Object1关系的DDL,对于Object2和Object3也是一样的:

    CREATE TABLE Something
    (
        ID INT PRIMARY KEY,
        .....
    )
    
    CREATE TABLE Object1
    (
       ID INT PRIMARY KEY,
       .....
    )
    
    CREATE TABLE Something_Object1
    (
        Something_ID INT PRIMARY KEY,
        Object1_ID INT NOT NULL,
        ......
    
        FOREIGN KEY (Something_ID) REFERENCES Something(ID),
        FOREIGN KEY (Object1_ID) REFERENCES Object1(ID)
    )
    

    此票multiple-foreign-keys-for-the-same-business-rule中其他可能选项的更多详细信息和示例

    【讨论】:

      【解决方案3】:

      两种最常见的方法是 Table Per Class(即一个表用于基类,另一个表用于每个子类,其中包含描述子类所需的附加列)和 Table Per Hierarchy(即一个表中的所有列,具有一个或多个列以允许区分子类)。哪种方法更好实际上取决于您的应用程序和数据访问策略的细节。

      您将在第一个示例中通过反转 FK 的方向并从父级中删除额外的 id 来获得每个类的表。另外两个本质上是每个类的表的变体。

      【讨论】:

      • 我相信您最有可能在这里描述继承而不是多态关联。是的,它们是相关的,因为当你确实有继承时你只有多态关联,但我仍然不确定如何解决关联部分。 Object1、Object2、Object3都继承或实现了“Object”,但是“Something”又该如何关联呢。
      • 在纯粹的数据库方式中,每个类的表将比每个层次结构的表更困难。如果您使用每个层次结构的表,那么您只需要一个 FK 到 Object。这将允许您以多态方式处理任何 Object 子类,并且您可以通过将鉴别器添加到 where 或 Join 来获取特定实例。
      【解决方案4】:

      根据我的说法,您的第一种方法是定义数据和类的最佳方式,但您的所有主要数据都应该对孩子有用。

      因此您可以检查您的需求并定义数据库。

      【讨论】:

        【解决方案5】:

        方法 1 是最好的,但某物与 object1、object2、object3 之间的关联应该是一对一的。

        我的意思是子表中的 FK (object1, object2, object3) 应该是非空唯一键或子表的主键。

        object1,object2,object3 可以有多态对象值。

        【讨论】:

          【解决方案6】:

          我使用了我猜你会称之为基表的方法。例如,我有用于姓名、地址和电话号码的表格,每个表格都有一个 PK 标识。然后我有一个主实体表entity(entityID) 和一个链接表:attribute(entityKey, attributeType, attributeKey),其中attributeKey 可以指向前三个表中的任何一个,具体取决于attributeType。

          一些优点:允许每个实体任意数量的姓名、地址和电话号码,易于添加新的属性类型,极端规范化,易于挖掘通用属性(即识别重复的人),其他一些特定于业务的安全优势

          缺点:构建简单结果集的相当复杂的查询使其难以管理(即,我很难雇用具有足够 T-SQL 能力的人);对于非常特定的用例而不是一般用例,性能是最佳的;查询优化可能很棘手

          在很长的职业生涯中,我已经在这种结构中生活了几年,除非我有同样奇怪的业务逻辑约束和访问模式,否则我会犹豫再次使用它。对于一般用途,我强烈建议您的类型表直接引用您的实体。即Entity(entityID), Name(NameID, EntityID, Name), Phone(PhoneID, EntityID, Phone), Email(EmailID, EntityID, Email)。您将有一些数据重复和一些常见的列,但它会更容易编程和优化。

          【讨论】:

            【解决方案7】:

            没有单一或通用的最佳实践来实现这一目标。这完全取决于应用程序需要的访问类型。

            我的建议是概述对这些表的预期访问类型:

            1. 您将使用 OR 层、存储过程还是动态 SQL?
            2. 您期望的记录数是多少?
            3. 不同子类之间的差异程度如何?有多少列?
            4. 您会进行汇总或其他复杂的报告吗?
            5. 您是否有用于报告的数据仓库?
            6. 您是否经常需要批量处理不同子类的记录? ...

            根据对这些问题的回答,我们可以制定出合适的解决方案。

            存储特定于子类的属性的另一种可能性是使用具有名称/值对的表。如果有大量不同的子类或子类中的特定字段不经常使用,这种方法特别有用。

            【讨论】:

              【解决方案8】:

              我使用了第一种方法。在极端负载下,“Something”表会成为瓶颈。

              我为我的不同对象采用了模板 DDL,并将属性特化附加到表定义的末尾。

              在数据库级别,如果我真的需要将我的不同类表示为“某事”记录集,那么我会在它们的顶部放置一个视图

              SELECT "Something" fields FROM object1
              UNION ALL
              SELECT "Something" fields FROM object2
              UNION ALL
              SELECT "Something" fields FROM object3
              

              鉴于您拥有三个独立的对象,挑战在于如何分配非冲突主键。通常人们使用 UUID/GUID,但在我的情况下,密钥是在应用程序中基于时间和机器生成的 64 位整数,以避免冲突。

              如果您采用这种方法,那么您可以避免“某物”对象导致锁定/阻塞的问题。

              如果你想改变“Something”对象,那么这可能会很尴尬,因为你有三个独立的对象,所有这些都需要改变它们的结构。

              总结一下。选项一在大多数情况下都可以正常工作,但在严重的负载下,您可能会观察到需要拆分设计的锁定阻塞。

              【讨论】:

                【解决方案9】:

                具有多列外键的方法 1 是最好的方法。因为这样您就可以与其他表建立预定义的连接 这使得脚本更容易选择、插入和更新数据。

                【讨论】:

                  猜你喜欢
                  • 2021-10-17
                  • 1970-01-01
                  • 2011-08-25
                  • 2012-02-28
                  • 2018-10-01
                  • 2013-08-10
                  • 2011-06-03
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多