tre*_*vor 10 schema normalization database-design best-practices relational-theory
我偶然发现了一个我不擅长的数据库设计问题,而我的首选 DBA 大师正在进行消防演习。
本质上,我有一个包含以下主键的表(为简洁起见,PK):
child_id integer
parent_id integer
date datetime
Run Code Online (Sandbox Code Playgroud)
child_id
和parent_id
是实体表的外键。“子”表本身也包含一个到“父”表的外键,而且,每个表child_id
总是引用与parent_id
上表预期相同的外键。事实上,事实证明有一些额外的代码使两者保持同步。
这使得这个过度热情的规范化新手说“我应该删除冗余!”
我分解为以下内容:
Table_1 PK:
child_id integer
date datetime
Table_2 PK:
parent_id integer
date datetime
Table_3: (already exists)
child_id integer PRIMARY KEY
parent_id integer FOREIGN KEY
Run Code Online (Sandbox Code Playgroud)
瞧,当我以自然的方式将这些人连接在一起时,我恢复了原始表。这是我的理解,使这个 5NF。
然而,现在我意识到有一个隐藏的商业规则。
通常,与给定关联的日期child_id
必须是与相应parent_id
. 您可以看到第一个表强制执行此规则。
我的分解不强制执行规则,因为您可以自由添加到表 1,直到日期变得太大。
这使我来到这里,有以下问题:
这是分解5NF吗?虽然我会说它允许插入异常,但它似乎也遵循 Wiki 示例,该示例本身遵循本指南。短语(强调我的)“我们可以从由三种不同记录类型组成的规范化形式重建所有真实事实”给了我特别的停顿,因为无论我注入多少垃圾Table_1
,自然连接仍然会忽略它。
假设我不喜欢这种分解(我不喜欢)。我坦率地承认,实际的解决方案是让表格和代码保持原样。但是,从理论上讲,有没有办法分解和/或添加约束,以便我摆脱第一个表并保留我的业务规则?
规范化基于函数依赖。函数依赖与语义有关;它们与数据的含义有关。当您将实际问题简化为“parent_id、child_id、date”的级别,并且不包含任何示例数据时,您确实限制了尽职尽责的数据库设计人员可以为您提供的帮助。
您在一个表中有一个键 {child_id, parent_id, date} 并且您在子表中有(似乎)一对唯一的 {child_id, parent_id} 这一事实并不一定意味着组合的一部分是多余的. 这可能意味着在以 {child_id, parent_id, date} 作为主键的表中,属性对 {child_id, parent_id} 应该首先引用子表。
如果是这种情况,您可以使用FOREIGN KEY (child_id, parent_id) REFERENCES child (child_id, parent_id)
. 为此,您需要对“child”表中的一对列 (child_id, parent_id) 进行 UNIQUE 约束,如果 child_id 是其主键,这应该不是问题。
但是,如果不知道数据的含义,就无法判断,而您是该线程中唯一知道这一点的人。(但我们很乐意让您向我们解释。)
就原始表而言,您似乎是在说child_id -> parent_id。如果是这样,为什么原始表中的 parent_id 是第一位?为什么键不只是 (child_id, date),带有对“子”表的外键引用?在我看来,您所谈论的那种冗余可能可以通过删除“parent_id”列来解决。
SQL DDL 和 INSERT 语句形式的示例数据可帮助我们为您提供帮助。DDL 和 INSERT 语句比描述更精确。
尝试这个...
(child_id,parent_id)
在子表中添加唯一约束(PK,FK:child_id, PK,FK:parent_id, PK:date)
保持原样,FK 位于新的唯一约束的 2 列上或者
(PK,FK:child_id, FK:parent_id)
与子表 1:1 的新表(PK,FK: child_id, PK,FK: parent_id, PK:date)
保持原样。但 FK 位于新表的 2 列上如果没有别的事,它可能会激励你......
如果我理解正确的话,它将删除冗余和代码......