您应该使用单表继承还是在视图中联合使用的多个表?

时间:2010-07-29 19:18:26

标签: sql sql-server design-patterns single-table-inheritance

假设你有一张笔记表。该说明可以是关于特定帐户,订单行或订单。

  • 有关该帐户的说明不适用于任何特定的订单或订单。
  • 有关订单行的注释也适用于父订单和附加到订单的帐户。
  • 订单上的注释也适用于附加的帐户,但不适用于订单。

注意表

[Id]          [int] IDENTITY(1,1) NOT NULL
[NoteTypeId]  [smallint] NOT NULL
[AccountId]   [int] NULL
[OrderId]     [int] NULL
[OrderLineId] [int] NULL,
[Note]        [varchar](300) NOT NULL

我的想法是,如果我查看客户,我可以看到所有相关的注释。最初,我为上面的每一个创建了一个注释表,并在视图中将它们联合起来。 这里的问题是编辑/删除记录。可以在特定项目或帐户或订单的通用备注视图中编辑/删除注释。这种方法更加困难。

然后我切换到Single Table Inheritance模式。我的notes表具有AccountId,OrderId和OrderLineId的可空值。我还添加了NoteTypeId以明确标识记录。更容易管理更新/删除方案。

我有一些问题&问题仍然是这种方法。

  • 完整性 - 虽然可以在SQL和/或代码中设置复杂约束,但大多数DBA都不喜欢STI方法。
  • 对一堆空值的想法进行了辩论(尽管我认为SQL 2008中的性能已基于空值的存储而得到改进)
  • RDBMS中的表不必代表代码中的对象。表中的规范化并不表示该表必须是唯一对象。我相信前两句是真的,你说什么?

在这里讨论了一些。 Is an overuse of nullable columns in a database a "code smell"?我不得不说我同意伊恩,但我也想要一些相反的观点。

3 个答案:

答案 0 :(得分:2)

  

虽然可以在SQL和/或代码中设置复杂约束,但大多数DBA都不喜欢STI方法。

因为您需要额外的逻辑(CHECK约束或触发器)来实现业务规则,即业务仅引用其中一个实体 - 账户,订单,订单行。

在每个实体和注释表之间实现多对多表是更具可扩展性的。

  • 不需要ALTER TABLE语句添加另一个可以为空的外键(有一个列限制,而不是大多数可能达到它)。
  • 单个音符记录可与多个实体相关联
  • 如果新实体&添加了多对多表格

答案 1 :(得分:0)

看来STI在你的情况下会好吗?如果我正确地阅读了您的要求,那么实体继承将是一个链:

注意< - AccountNote(AccountId)< - AccountAndOrderNote(OrderId)< -AccountAndOrderAndOrderLineNote(OrderLineId)

完整性: 肯定不是问题? AccountId,OrderId和OrderLineId中的每一个都可以FK到它们各自的表(或者为NULL) 另一方面,如果你删除了AccountId,OrderId和OrderLineId(我不推荐BTW!)而只是ObjectId和NoteTypeId,那么你就无法添加RI而且会有一个非常混乱的CASE WHEN类型Join。

性能: 既然你说AccountId必须始终存在,我想它可能是非空的,因为OrderLine不能没有Order,所以(AccountId,OrderId)或(AccountId,OrderId,OrderLineId)的索引似乎有意义(取决于平均每个订单#OrderLines的可选择性与狭隘权衡

但OMG小马是关于凌乱的ALTER TABLEs将其扩展到新的音符类型是正确的,如果新音符不是由帐户派生的,索引将导致令人头疼。

HTH

答案 2 :(得分:0)

  

最初我[创建]单独的笔记   以上各项的表格   在一个视图中将它们联合起来。

这让我想知道你是否考虑过使用没有NULLable列的多表结构,其中每个注释都获得一个唯一的ID而不管类型如何。您可以在查询中的“单表继承”(或类似)中显示数据,而不使用UNION

以下是建议的结构。我已将NoteTypeId更改为VARCHAR,以使不同的类型更清晰,更易于阅读(您无论如何都没有枚举INTEGER值):

CREATE TABLE Notes
(
 Id INTEGER IDENTITY(1,1) NOT NULL UNIQUE, 
 NoteType VARCHAR(11) NOT NULL
    CHECK (NoteType IN ('Account', 'Order', 'Order line')), 
 Note VARCHAR(300) NOT NULL, 
 UNIQUE (Id, NoteType)
);

CREATE TABLE AccountNotes
(
 Id INTEGER NOT NULL UNIQUE, 
 NoteType VARCHAR(11) 
    DEFAULT 'Account' 
    NOT NULL
    CHECK (NoteType = 'account'),
 FOREIGN KEY (Id, NoteType)
    REFERENCES Notes (Id, NoteType)
       ON DELETE CASCADE, 
 AccountId INTEGER NOT NULL
    REFERENCES Accounts (AccountId)
);

CREATE TABLE OrderNotes
(
 Id INTEGER NOT NULL UNIQUE, 
 NoteType VARCHAR(11) 
    DEFAULT 'Order'
    NOT NULL
    CHECK (NoteType = 'Order'),
 FOREIGN KEY (Id, NoteType)
    REFERENCES Notes (Id, NoteType)
       ON DELETE CASCADE, 
 OrderId INTEGER NOT NULL
    REFERENCES Orders (OrderId)
);

CREATE TABLE OrderLineNotes
(
 Id INTEGER NOT NULL UNIQUE, 
 NoteType VARCHAR(11) 
    DEFAULT 'Order line'
    NOT NULL
    CHECK (NoteType = 'Order line'),
 FOREIGN KEY (Id, NoteType)
    REFERENCES Notes (Id, NoteType)
       ON DELETE CASCADE, 
 OrderLineId INTEGER NOT NULL
    REFERENCES OrderLines (OrderLineId)
);

在“单表继承”结构中显示数据(即所有JOIN和无UNION):

SELECT N1.Id, N1.NoteType, N1.Note, 
       AN1.AccountId, 
       ON1.OrderId, 
       OLN1.OrderLineId
  FROM Notes AS N1
       LEFT OUTER JOIN AccountNotes AS AN1
          ON N1.Id = AN1.Id
       LEFT OUTER JOIN OrderNotes AS ON1
          ON N1.Id = ON1.Id
       LEFT OUTER JOIN OrderLineNotes AS OLN1
          ON N1.Id = OLN1.Id;

考虑上述结构具有完整的数据完整性约束。要使用“单表继承”结构执行相同操作,需要更多CHECK个约束,其中有许多条件可用于可为空的列,例如:

CHECK (
       (
        AccountId IS NOT NULL
        AND OrderId IS NULL
        AND OrderLineId IS NULL
       )
       OR
       (
        AccountId IS NULL
        AND OrderId IS NOT NULL
        AND OrderLineId IS NULL
       )
       OR
       (
        AccountId IS NULL
        AND OrderId IS NULL
        AND OrderLineId IS NOT NULL
       )
      );

CHECK (
       (
        NoteType = 'Account'
        AND AccountId IS NOT NULL
       )
       OR
       (
        NoteType = 'Order'
        AND OrderId IS NOT NULL
       )
       OR 
       (
        NoteType = 'Order line'
        AND OrdereLineId IS NOT NULL
       )
      );

etc etc

我敢打赌,大多数使用“单表继承”的应用程序开发人员都不会为创建这些数据完整性限制而烦恼,如果他们发生这样做的话(这并不意味着粗鲁,只是优先级不同)对我们更关心'后端'而不是'前端'的人:)