一旦遇到循环警告,在不更改数据库结构的情况下确保引用完整性的唯一另一种方法是对其中一个约束使用触发器。触发警告的问题是双重的:
- 该表与该
Sections
表共享两个相反方向的外键约束Persons
。
- 所有四个表都有一个完整的循环,所有这些表都以相同的方向进行,Person->Section->Department->Organization。
此外,表格设计存在一个问题:它的构造方式,一个人可能负责他不属于的部分。
InCharge
您可以通过从 中删除列Sections
并引入另一个表来解决这三个问题SectionInCharge
, 包含列PerID
和SecID
,在 上具有聚集索引/唯一约束,并且在两列上都具有表SecID
的复合外键。Persons
您需要向表中的两列添加 UNIQUE 约束,Persons
以便 FK 可以与它们相关联。
现在您可以将新的 FK 设置为 ON DELETE CASCADE 并删除。
使用行的存在或不存在来表示某事而不是使用可为空的列通常是一种优越的数据库设计模式。
现在我想一想,其他带有“负责人”列的表在允许分配不正确的成员的人方面也存在类似的问题。我知道使用 FK 解决这个问题的唯一方法(在我看来这是触发器的最佳实践)是通过表传播列并将它们包含在复合键中。这具有使事情变得有点尴尬的不幸副作用,但它还不错,因为您可以将最具体的列放在第一位,并仅在可能的情况下加入它。因此,您可以将 OrgID 放入Section
表中,并在 Dept 表上使用复合 FK (DeptID, OrgID)
,然后将DeptID
andOrgID
也放入Persons
表中。然后,您将需要DeptInCharge
和OrgInCharge
表。
您可能感兴趣的关于这一点(这并不是真正解决您提出的核心问题)的另一个注意事项是将您的三个底部表格组合成一个表格的可能性,OrgUnit
. 添加一个OrgUnitType
列和一个 ParentOrgID 列。当事物不具有该属性时添加将为 NULL 的列(如在没有传真号码的部分中),或者使现有的组织、部门和部分表成为 OrgUnit的子类型(数据库超类型/子类型的示例)。这样做的好处是,你可以在任何地方引用这三种类型中的任何一种:如果 FK 是 OrgUnit,它可以是其中任何一种。但是,如果它只允许作为一个部分,那么您将改为对那个表进行 FK。事实上,这对于单身人士来说是完美的OrgUnitInCharge
表,带有 FK 到OrgUnit (OrgUnitID, OrgUnitTypeID)
.
我知道这里有一些松散的结尾,我还没有将它们完全捆绑成一个漂亮的包给你,但我希望到目前为止我所描述的内容是有帮助的。
PS我希望你能原谅我增加了风格上的考虑。对我来说,在每个表的每一列上放置一个表表示前缀是很多额外的不必要的装饰。根据我的感觉,我输入 90-110 wpm,但我仍然讨厌打字。此外,我坚信在我的代码中保持高信噪比,而对我来说,到处爆炸的前缀大大降低了这一点。在 SQL 查询中,最佳实践是使用表别名,然后在每列上使用这些别名,如SELECT P.Name FROM dbo.Person P
. 为了解决多个表具有相同列名的问题,我已经开始将表中的典型WidgetName
列Widget
也设置为Widget
. 作为为什么高信噪比如此重要的一个例子: vrbaux 可以让您阅读艺术这句话 advQuickly conAnd advEasily?确实,附加前缀包含有用的信息,但这些有用的信息只有一点用处,而且很分散注意力。如果您现在从头开始设计数据库,我想请求您不要使用这些前缀,因为某个奇怪的巧合有一天最终会维护它(发生了奇怪的事情)。:)
PPS 我喜欢 PascalCase 而不是 under_scores,因为它更容易输入。下划线位于最上面一行并被最弱的手指击中 - 所以它更慢且容易出错。
PPPS 我也更喜欢单数表名,但这既不是这里也不是那里?我提前为你对风格的批评感到抱歉。