首选数据库规范化还是架构透明性?


10

新的要求出现在旧的代码库上,该代码库基本上实现了两个以前不直接相关的用户类之间的直接(内部)通信(存储在具有完全不同模式的不同表中,并且可悲的是,该代码几乎不面向对象)设计较少,因此没有父类)。由于我们不愿使用这种从未考虑过此功能的旧设置,因此无法保证不存在PK冲突-在使用数据集的情况下,实际上可以保证存在ARE。

因此,解决方案似乎很明显:用火杀死它并重写整个混乱的 A映射表。我已经为实现地图的可能方法提供了两个指导,但是我不是DBA,所以我不确定是否有我遗漏的利弊。

为了阐明抽象,请考虑三组不同的用户数据:教授,行政管理和学生(不,这不是一项家庭作业。应许!)

映射1

(professor_id,admin_id和student_id是它们各自表的外键)

| mailing_id (KEY) | professor_id | admin_id | student_id | 
-------------------------------------------------------
| 1001             |     NULL     |    87    |  NULL      |
| 1002             |     123      |   NULL   |  NULL      |
| 1003             |     NULL     |   NULL   |  123       |

这种方法的+/-在缺点上非常繁重:

  • 每行两个“浪费”字段
  • 违反2NF
  • 容易插入/更新异常(例如,只有0-1字段设置为NULL的行)

但是,专业人士并非没有自己的优点:

  • 可以通过一次查找完成映射
  • 从mailing_id轻松确定给定用户的“源”数据

说实话,直言不讳,我一点都不喜欢这个主意。

对应2

(假设MSG_ *是定义的常量,枚举类型或其他合适的标识符)

| mailing_id (KEY)  | user_type (UNIQUE1) | internal_id (UNIQUE2)| 
------------------------------------------------------------------
| 1001              | MSG_ADMIN          | 87                    |
| 1002              | MSG_PROF           | 123                   |
| 1003              | MSG_STUDENT        | 123                   |

通过此设置,{user_type,internal_id}的唯一复合索引使情况变得更加清晰,维护了3NF,并且应用程序代码不必检查I / U异常。

不利的一面是,在确定必须在数据库外部处理的用户源表时,会损失一些透明度,这基本上相当于将user_type值映射到表的应用程序级。现在,我(相当强烈)倾向于第二种映射,因为缺点很小。

但是我很痛苦地意识到自己的局限性,并且确定自己可能已经错过了两个方面的优势或绊脚石,因此我转向比我更明智的想法。


2
您可能会发现Martin Fowler关于角色的想法很有趣。
Marjan Venema 2013年

确实,这很有趣。遗憾的是,对于我的具体问题没有太多的见识
GeminiDomino

您将获得成为行政管理人员的教授和在行政管理部门中任职的学生,甚至在10年后重返教职。您可能已经拥有了它们。您是将这些分隔开还是尝试统一?
艾琳(Elin)2013年

角色只是示例,但我明白你的意思。在实践中,即使用户确实切换了角色,他们仍将作为单独的记录保留。
GeminiDomino

如果您重新措辞第一段,那就太好了。还不清楚。我的意思是,很明显这是有问题的,但目前还不清楚。
图兰斯·科尔多瓦

Answers:


1

您的第二个想法是正确的。 这种方法使您可以完成整合三个碰撞键空间所需的所有映射。

重要的是,它允许数据库使用声明性约束强加您需要具备的大多数一致性。

您已经拥有了比想要的更多的代码,所以不要添加超出绝对必要数量的代码来保持集成密钥列表的一致性。让您的数据库引擎执行其构建工作。

在“ 映射2 ”中使您感到不适的“问题孩子” 是该USER_TYPE列。此列很重要,因为您需要它以确保INTERNAL_ID每个用户类型最多只出现一次。您唯一需要甚至不知道USER_TYPE的代码就是在映射表中插入和删除的代码。这可以很好地本地化。我假设您将在代码中创建一个维护映射表内容的点。在这一点上数据的地方没有多余的列。您真正要避免的是在读取数据的所有位置添加额外的列。

USER_TYPE通过为每个子应用程序提供一个视图,可以将映射过滤到一个特定于应用程序的特定用户类型,子应用程序中需要使用映射的代码就可以完全忽略了。


3

根据经验,我的建议是选择一致性而不是优雅或“最佳实践”。那是为了匹配现有设计,并使用三个具有简单mailing_id, user_id字段结构的邮件表(每个角色一个)。

它不雅致,但有一些优点...

  1. 对于将在将该模式发布到牧场之前使用该模式的任何人,匹配现有结构都将更加容易。
  2. 您没有浪费的字段,也没有在要求数据库匹配不存在的内容。
  3. 因为每个表只能互相访问,所以创建一个将所有数据联系起来供例程使用的视图相对容易。

我敢肯定,还有很多人会不同意这种方法,但是规范化和最佳实践的主要目的是使代码更加一致,以便于遵循和调试……而且显然,将整个代码库重新构建起来可能是不可行的。


这种方法的问题在于数据库无法在邮件ID中强制唯一性,这首先是映射的主要目的:否则,可以将每个表中的各个ID字段与“用户类型”指示器配对完成没有任何变化。
GeminiDomino

我确实看到了您的意思,但是在这种系统上工作时,我提供了您可能没有考虑过的选项。就我所知,邮寄ID需要一些内容来引用某个地方(邮寄的内容或如何查找文档),因此无论如何邮寄ID应该是外键,这意味着唯一性问题将在其他地方解决。在我读到它时,链接到的admin学生表和prof数据表可能具有不同的结构,因此我看不到用户类型字段会增加价值。原始开发人员一定遇到了这个问题,他们做了什么?
James Snell 2014年

“用户类型”字段将确定与该特定记录关联的表。无论哪种方式,都必须在应用程序级别进行处理,并且由于它们位于不同的表中,因此没有很好的方法使其成为外键约束。不幸的是,原始开发人员似乎根本没有考虑过这个问题,这就是为什么它变得如此混乱的原因。:)
GeminiDomino
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.