3

假设您有一张便笺表。注释可以是关于特定帐户、订单行或订单的。

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

注释表

[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

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

然后我切换到单表继承模式。我的 notes 表具有 AccountId、OrderId 和 OrderLineId 的可为空值。我还添加了 NoteTypeId 来明确识别记录。更容易管理更新/删除方案。

我对这种方法仍有一些问题和疑问。

  • 完整性 - 尽管可以在 SQL 和/或代码中设置复杂的约束,但大多数 DBA 不喜欢 STI 方法。
  • 一堆空值的想法是有争议的(尽管我相信 SQL 2008 中的性能基于空值的存储有所提高)
  • RDBMS 中的表不必在代码中表示对象。表中的规范化并不是说表必须是唯一的对象。我相信前两句话是真的,你说什么?

这里讨论了一些。 过度使用数据库中的可空列是“代码异味”吗?我不得不说我同意伊恩的观点,但我也喜欢一些相反的观点。

4

3 回答 3

2

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

因为您需要额外的逻辑(CHECK 约束或触发器)来实现注释仅引用实体之一的业务规则 - 帐户、订单、订单行。

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

  • 不需要 ALTER TABLE 语句来添加另一个可为空的外键(存在列限制,不是大多数人都可能达到)
  • 单个笔记记录可以与多个实体相关联
  • 如果添加新实体和多对多表,对现有记录没有影响
于 2010-07-29T19:30:55.057 回答
0

在您的情况下,STI 似乎可以正常工作?如果我正确阅读了您的要求,实体继承将是一个链:

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

诚信:肯定不是问题?AccountId、OrderId 和 OrderLineId 中的每一个都可以对它们各自的表进行 FK(或为 NULL)如果另一方面,如果您删除了 AccountId、OrderId 和 OrderLineId(我不推荐顺便说一句!)而只是 ObjectId 和 NoteTypeId , 那么你不能添加 RI 并且在输入 Join 时会有一个非常混乱的 CASE。

性能:既然您说 AccountId 必须始终存在,我猜它可能是非空的,并且由于没有 Order 就无法存在 OrderLine,因此 (AccountId, OrderId) 或 (AccountId, OrderId, OrderLineId) 的索引似乎是有道理的(取决于每个订单的平均#OrderLines 的选择性与窄度权衡)

但是 OMG Ponies 对凌乱的 ALTER TABLE 将其扩展到新的笔记类型是正确的,如果新笔记不是来自帐户的,那么索引将导致头痛。

高温高压

于 2010-07-29T21:08:19.057 回答
0

最初,我为上述每个 [创建] 单独的注释表,并将它们合并到一个视图中。

这让我想知道您是否考虑过使用没有 NULLable 列的多表结构,其中每个注释都获得一个唯一 ID,而不管类型如何。您可以在不使用UNION.

下面是一个建议的结构。我已更改为 VARCHAR 以使不同的类型更清晰、更易于阅读(无论如何NoteTypeId您都没有枚举值):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)
);

以“单表继承”结构(即所有JOINs 和没有UNIONs)呈现数据:

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

我敢打赌,大多数使用“单表继承”的应用程序开发人员根本不会费心去创建这些数据完整性约束,如果他们想到这样做的话(这听起来并不粗鲁,只是对我们来说,优先级有所不同)比“前端”更关心“后端”:)

于 2010-07-30T09:52:17.067 回答