我一直在努力构建一个更抽象的架构,其中有几个表建模非常相似的关系,我想模拟“本质”。由于我正在使用的环境(Drupal 7),我无法改变问题的性质:相同基本类型的关系可以在一个角色中引用对象的两个不同表中的一个。让我们举一些例子来澄清(这不是我的实际问题域,但是类似的问题)。以下是要求:
首先,如果你不熟悉Drupal,那么这就是要点:一个表中的用户,单个第二个表中的每个其他实体(粗略概括,但足够)。
假设我们想要模拟“为...而工作”的关系,让我们给出“公司”类型为“实体”而“主管”属于“用户”类型(以及“类型”我的意思)这是他们的元组所在的数据库中的表)。以下是简化要求:
我有两个想法,两者并不完全符合我目前对架构质量的态度,这是我想要一些见解的地方。
如果您是视觉思考者,以下是两个选项,表示用户123和632以及实体123都为实体435工作的事实:
Option 1
+---------------+-------------+---------------+-------------+
| employment_id | employee_id | employee_type | employer_id |
+---------------+-------------+---------------+-------------+
| 1 | 123 | user | 435 |
+---------------+-------------+---------------+-------------+
| 2 | 123 | entity | 435 |
+---------------+-------------+---------------+-------------+
| 3 | 632 | user | 435 |
+---------------+-------------+---------------+-------------+
Option 2
+---------------+------------------+--------------------+-------------+
| employment_id | employee_user_id | employee_entity_id | employer_id |
+---------------+------------------+--------------------+-------------+
| 1 | 123 | <NULL> | 435 |
+---------------+------------------+--------------------+-------------+
| 2 | <NULL> | 123 | 435 |
+---------------+------------------+--------------------+-------------+
| 3 | 632 | <NULL> | 435 |
+---------------+------------------+--------------------+-------------+
关于选项1的想法:我喜欢employee_id列具有具体角色,但我鄙视它有模糊目标。选项2的作用不明确(哪一列是员工?),但是对于任何给定的FK都有具体的目标,所以我可以这样想:
+-----------+-----------+----------+
| | ROLE |
| | ambiguous | concrete |
+-----------+-----------+----------+
| T | | |
| A ambig. | | 1 |
| R | | |
| G -------+-----------+----------+
| E | | |
| T concr. | 2 | ? |
| | | |
+-----------+-----------+----------+
选项二对我的项目有非常实用的好处,但是我对这么多空值感到不舒服(你甚至可能不称它为1NF!)
所以这里是我的问题的关键所在:如何改进选项1,或者我可能有什么知识差距让我感到不安?虽然我无法想到它违反的特定规则,但设计显然不符合规范化的意图(需要两列来唯一地识别关系对我没有任何帮助)防止异常情况。)
我确实理解理想解决方案是将用户实体重新设计为相同,就像我一直称之为“实体” “在这里,但请考虑旁边的点/间接(或者至少让我们在这个问题的确切位置画出实用的线)。
同样,基本问题:在规范化方面,什么是模式选项1的错误,以及如何在不将“用户”重构为“实体”的约束下对此关系进行建模?
注意: 为此,我对理论纯度比对实用解决方案更感兴趣
答案 0 :(得分:1)
你提出的解决方案与@podiluska所说的相反,违反了第4范式。如果将其改写为下面的表格,那么解决方案会消除这种困难,并且在5NF(甚至6NF?)中。
采用其中一种子/超类型的模式。这使用下面列出的关系定义,加上超/子类型约束。这个约束是超类型关系中的每个元组必须完全对应于一个子类型元组。换句话说,子类型必须形成一个不相交的,覆盖超类型的集合。
我怀疑这种情况在实际情况下的表现可能需要进行一些重大调整:
Table: Employment
+---------------+-------------+
| employee_id | employer_id |
+---------------+-------------+
| 1 | 435 |
+---------------+-------------+
| 2 | 435 |
+---------------+-------------+
| 3 | 435 |
+---------------+-------------+
Table: Employee (SuperType)
+---------------+
| employee_id |
+---------------+
| 1 |
+---------------+
| 2 |
+---------------+
| 3 |
+---------------+
Table: User employee (SubType)
+---------------+-------------+
| employee_id | user_id |
+---------------+-------------+
| 1 | 123 |
+---------------+-------------+
| 3 | 632 |
+---------------+-------------+
Table: Entity employee (SubType)
+---------------+-------------+
| employee_id | entity_id |
+---------------+-------------+
| 2 | 123 |
+---------------+-------------+
答案 1 :(得分:0)
选项1(和选项2)的错误在于它是多值依赖关系,因此违反了第4范式。但是,在你给出的限制范围内,你可以做很多事情。
如果您可以用视图替换worksfor
表,那么您可以将用户 - 公司和公司 - 公司关系分开。
在您的两个选择中,选项2的优势在于可能更容易实施参照完整性,具体取决于您的平台。
当前约束中的一个潜在的,如果icky,务实的解决方案可能是为公司提供正ID和用户负ID,这消除了选项2的空列并将选项1的类型列转换为含义,但我觉得很脏甚至暗示它。
同样,如果您不需要知道实体的类型,只要您可以通过加入来确定它,那么使用Guids作为ID将不再需要type
列