0

我正在与一位刚接触编程的同事一起工作,我正在尝试解释 ORM(和 SRP)的一些概念,但不知何故我失败了。这是一个 Rails 应用程序。

我正在使用的应用程序的类层次结构如下所示:

-CallFlow——
路由(多态)
——RouteOptions

有特定于各种 Route 的属性,每个 RouteType 都有自己的一组选项。理想情况下,对我来说,会有一个 call_flows 表,每个路由类型的表,然后对于具有选项的路由类型,该路由类型的选项表。一个非常粗糙的模型:

在此处输入图像描述

Simple_route 和 outbound_route 没有选项。在确实有选项的路线中,它是一个 has_many 关系,为我们提供了该路线的一系列选项。

相反,我的同事想将特定于每个路由的所有字段放在 call_flows 表中。这是该模式的模型:

在此处输入图像描述

因此,您将拥有一个大的 call_flows 表,其中的字段并不适用于每条记录。事实上,只有少数人愿意。我的建模决定背后的原因如下:

  • 它遵循基本的标准化模式
  • 它减少了空值的数量,从而减少了数据库大小
  • 改变更灵活
  • 它遵循基本的SRP原则

我错过了什么吗?如果有任何资源可以帮助新程序员了解 DB 规范化的重要性,那就太好了。

谢谢!

4

3 回答 3

3

标准化背后的核心概念是Data Dependencyvs Functional Dependency。在规范化模式中,只有依赖关系是功能依赖关系,由业务或数据语义决定。例如,RouteOptions 依赖于 Route。

在非规范化表中,由于所有数据都存储在所有地方,因此将存在数据依赖以及函数依赖。在这种情况下,很难执行事务并以 100% 的信心断言您的数据模型是一致的。

让我们举个例子。您想添加一个新的路线选项。这是你的交易。在规范化表中,您在 RouteOption 表中创建一条新记录,并将 Route_ID 填写在同一记录中。您 100% 确信您的数据模型在事务之后是一致的。

采用非规范化模式,假设您有相同的 RoutOption 表和 RouteSummary 表,其中包含来自多个表的列。当您在此架构上执行上述事务时,您的数据模型并不一致。您必须“记住”才能更新 RouteSummary 表。几天后,将创建一些其他表,该表也将具有 RouteOption。先前存在的事务将不知道这个新表。

非规范化模式也有一个地方。当您的数据存储不是事务性的时。如果它是 ReadOnly 模式,主要用于分析原因,则不会有事务,因此不会有数据不一致的风险。因此,它们在该用例中是可以的。

于 2012-09-06T21:23:07.583 回答
1

我真的不明白通过拥有额外的 *_options 表可以获得什么。即这里的内容不能只进入表中的路线类型。

话虽如此,您使用不同的路由类型、选项等描述的那种复杂结构对我来说似乎更适合 NoSQL、无模式的存储方法。在这种情况下,您可以只拥有一个路由集合,每个集合在属性、选项等方面都可以有自己独特的结构。

于 2012-09-06T20:58:23.473 回答
1

您的案例看起来像是称为“泛化专业化”或简称 Gen-Spec 的设计模式的一个实例。如何使用数据库表对 gen-spec 建模的问题一直出现在 SO 中。

如果您在 Java 等 OOPL 中对 gen-spec 进行建模,您将使用子类继承工具来为您处理细节。您只需定义一个类来处理泛化对象,然后定义一个子类集合,每个子类都扩展其父超类。它简单明了。

不幸的是,据我所知,关系数据模型没有内置子类继承,并且 SQL 数据库系统不提供任何此类工具。但你并不是不走运。您可以设计您的表以与 OOP 的类结构相似的方式对 gen-spec 进行建模。当新项目添加到通用类时,您必须安排实现自己的继承机制。详情如下。

Martin Fowler 确定了三种不同的模仿继承的表格设计。您喜欢的设计接近于 Fowler 所说的 Class Table Inheritance。您的同事喜欢的设计接近于 fowler 所说的Single Table Inheritance。每个都有其优点和缺点。

类表继承的有趣部分称为共享主键。在这里,子类表有一个具有双重职责的键。它是主键,也是返回超类表的外键引用。当创建新条目时,您的应用程序必须将键值从超类表传播到适当的子类表。

当您需要将数据重新连接在一起时,就会出现平滑的部分。基于共享主键的加入操作非常灵活、简单且快速。

于 2012-09-07T06:26:10.917 回答