假设你有一张笔记表。该说明可以是关于特定帐户,订单行或订单。
[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以明确标识记录。更容易管理更新/删除方案。
我有一些问题&问题仍然是这种方法。
在这里讨论了一些。 Is an overuse of nullable columns in a database a "code smell"?我不得不说我同意伊恩,但我也想要一些相反的观点。
答案 0 :(得分:2)
虽然可以在SQL和/或代码中设置复杂约束,但大多数DBA都不喜欢STI方法。
因为您需要额外的逻辑(CHECK约束或触发器)来实现业务规则,即业务仅引用其中一个实体 - 账户,订单,订单行。
在每个实体和注释表之间实现多对多表是更具可扩展性的。
答案 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
我敢打赌,大多数使用“单表继承”的应用程序开发人员都不会为创建这些数据完整性限制而烦恼,如果他们发生这样做的话(这并不意味着粗鲁,只是优先级不同)对我们更关心'后端'而不是'前端'的人:)