4

我的雇主是一家小型办公用品公司,正在更换供应商,我正在查看他们的电子内容以提出一个强大的数据库架构;我们之前的模式几乎是完全不加思索地拼凑在一起的,而且它几乎导致了一个无法忍受的数据模型,其中包含损坏的、不一致的信息。

新供应商的数据比旧供应商的数据要好得多,但他们的数据就是我所说的超标准化。例如,他们的产品类别结构有5个层次:Master Department、Department、Class、Subclass、Product Block。此外,产品块内容具有产品的长描述、搜索词和图像名称(想法是产品块包含产品和所有变体 - 例如,特定的笔可能采用黑色、蓝色或红色墨水;所有这些items 本质上是相同的东西,因此它们适用于单个产品块)。在我得到的数据中,这表示为产品表(我说“表”,但它是一个包含数据的平面文件),它引用了产品块的唯一 ID。

我正在尝试提出一个强大的模式来容纳我提供的数据,因为我需要相对较快地加载它,而且他们给我的数据似乎与他们的数据类型不匹配在他们的示例网站 ( http://www.iteminfo.com )上提供演示。无论如何,我不打算重用他们的演示结构,所以这是一个有争议的问题,但我正在浏览该网站以了解如何构建事物的一些想法。

我不确定是否应该以这种格式保存数据,或者例如使用自引用关系将主/部门/类/子类合并到一个“类别”表中,并将其链接到产品块(产品块应该分开,因为它不是一个“类别”,而是给定类别的一组相关产品)。目前,产品块表引用子类表,因此如果我将它们合并在一起,这将更改为“category_id”。

我可能会创建一个电子商务店面,利用 Ruby on Rails 上的这些数据(或者这是我的计划,无论如何),所以我试图避免以后被卡住或拥有一个臃肿的应用程序 - 也许我我想太多了,但我宁愿安全也不愿后悔;我们之前的数据一团糟,由于数据不一致和不准确,公司损失了数万美元的销售额。此外,我将通过确保我的数据库是健壮的并强制执行约束来稍微打破 Rails 约定(我也计划在应用程序级别这样做),所以这也是我需要考虑的事情。

你会如何处理这样的情况?请记住,我已经将数据加载到模拟表结构的平面文件中(我有文档说明哪些列是哪些列以及设置了哪些引用);我正在尝试决定是否应该让它们像目前一样正常化,或者我是否应该寻求整合;我需要知道每种方法将如何影响我使用 Rails 对网站进行编程的方式,因为如果我合并,一个表中基本上会有 4 个“级别”的类别,但这似乎比单独的表更易于管理每个级别,因为除了子类(直接链接到产品块)他们不做除了显示它们下的下一级类别之外的任何内容。我总是对处理这样的数据的“最佳”方式感到茫然——我知道“规范化直到它受伤,然后非规范化直到它起作用”的说法,但直到现在我才真正需要实施它。

4

10 回答 10

6

我更喜欢“超规范化”方法而不是非规范数据模型。您提到的自引用表可能会减少表的数量并在某些方面简化生活,但通常这种类型的关系可能很难处理。分层查询变得很痛苦,将对象模型映射到此(如果您决定走那条路线)也是如此。

几个额外的连接不会受到伤害,并且会使应用程序更易于维护。除非由于连接数量过多而导致性能下降,否则我会选择保持原样。作为额外的好处,如果这些级别的表中的任何一个需要添加额外的功能,您将不会遇到问题,因为您将它们全部合并到自引用表中。

于 2009-02-03T16:10:34.770 回答
3

我完全不同意对父子层次结构的自引用表结构的批评。在大多数情况下,链表结构使 UI 和业务层编程更容易且更易于维护,因为链表和树是用通常用于实现 UI 和业务层的语言来表示这些数据的自然方式。

关于在这些结构上维护数据完整性约束的难度的批评是完全正确的,尽管简单的解决方案是使用一个包含更难检查约束的闭包表。使用触发器很容易维护闭包表。

权衡是数据库(闭包表和触发器)中的一些额外复杂性,而 UI 和业务层代码的复杂性要低得多。

于 2009-02-03T19:15:43.803 回答
2

如果我理解正确,您希望将它们单独的表转换为一个层次结构,该层次结构保存在具有自引用 FK 的单个表中。

这通常是一种更灵活的方法(例如,如果您想添加第五层),但 SQL 和关系数据模型往往不能很好地处理像这样的链表,即使是像 MS SQL Servers CTE 这样的新语法。诚然,CTE 让它变得更好。

执行某些事情可能既困难又昂贵,例如产品必须始终位于层次结构的第四层等。

如果您确实决定这样做,那么一定要查看 Joe Celko 的Smarties SQL,我相信其中有一两节是关于建模和使用 SQL 中的层次结构的,或者更好的是获得他专门针对该主题的书(Joe Celko 为 Smarties 编写的 SQL 中的树和层次结构)。

于 2009-02-03T16:10:01.620 回答
2

Normalization意味着数据完整性,即:每个范式都会减少数据不一致的情况。

通常,denormalization它的目标是更快querying,但会导致空间增加、DML时间增加,最后但并非最不重要的一点是,需要加大努力使数据保持一致。

人们通常会更快地编写代码(写得更快,而不是代码更快),如果数据是normalized.

于 2009-02-03T16:18:18.887 回答
2

自引用表几乎总是比规范化表更难查询和执行。不要这样做。它可能看起来更优雅,但它不是而且是一种非常糟糕的数据库设计技术。就我个人而言,您描述的结构对我来说听起来不错,没有超规范化。正确规范化的数据库(具有外键约束以及默认值、触发器(如果需要复杂规则)和数据验证约束)也更有可能拥有一致和准确的数据。我同意让数据库执行规则,这可能是最后一个应用程序有错误数据的部分原因,因为规则没有在适当的地方执行,人们能够轻松绕过它们。并不是说应用程序不应该 t 检查(甚至发送无效日期以使数据库在插入时失败)。自从你重新设计以来,我会花更多的时间和精力来设计必要的约束和选择正确的数据类型(例如,不要将日期存储为字符串数据),而不是试图让完美的普通规范化结构看起来更优雅。

于 2009-02-03T18:05:32.713 回答
1

我会尽可能地把它带入他们的模型(如果可能的话,我会得到与他们的模式匹配的文件——而不是扁平化的版本)。如果您将数据直接带入您的模型,如果他们发送的数据开始打破转换到您的内部应用程序模型的假设,会发生什么情况?

最好将他们的数据带入,运行健全性检查并检查假设没有被违反。然后,如果您确实有一个特定于应用程序的模型,请将其转换为您的应用程序最佳使用的模型。

于 2009-02-03T16:16:18.113 回答
0

不要反规范化。试图通过非规范化来实现良好的模式设计,就像试图从纽约开车去旧金山一样。它不会告诉你走哪条路。

在您的情况下,您想弄清楚规范化模式想要什么。您可以在很大程度上基于源模式,但您需要了解数据中的功能依赖关系 (FD) 是什么。源架构和展平文件都不能保证向您显示所有 FD。

一旦您知道规范化模式的外观,您现在需要弄清楚如何设计满足您需求的模式。它的模式还没有完全规范化,就这样吧。但是要准备好在扁平文件中的数据与您设计的模式中的数据之间进行转换时遇到困难。

您说贵公司以前的模式由于不一致和不准确而花费了数百万美元。您的模式越规范化,您就越能免受内部不一致的影响。这使您可以自由地对不准确性更加警惕。始终错误的一致数据可能与不一致的数据一样具有误导性。

于 2009-02-04T11:33:12.707 回答
0

您的店面(或您正在建造的任何东西,对此不太清楚)是否总是会使用来自该供应商的数据?您是否会更换供应商或添加其他不同的供应商?

如果是这样,请设计一个满足需求的通用模式,并将供应商数据映射到它。就我个人而言,我宁愿忍受自引用类别(分层)表的(非常轻微的)“痛苦”,也不愿维持四个(显然是半无用的)类别变体级别,然后明年发现他们已经添加了第 5 个,或者推出了一条只有三个的产品线……

于 2010-11-24T01:43:28.933 回答
0

对我来说,真正的问题是:什么更适合模型?

这就像比较一个元组和一个列表。

  1. 元组是固定大小并且是异构的——它们是“超规范化的”。
  2. 列表是任意大小并且是同质的。

当我需要一个元组时我使用一个元组,当我需要一个列表时我使用一个列表;它们从根本上服务于不同的目的。

在这种情况下,由于产品结构已经很好地定义(并且我认为不太可能改变),所以我会坚持使用“元组方法”。列表(或递归表模式)的真正威力/用途是当您需要它扩展到任意深度时,例如用于 BOM 或家谱树。

我根据需要在我的一些数据库中使用这两种方法。然而,递归模式也存在“隐藏成本”,即并非所有 ORM(不确定 AR)都很好地支持它。许多现代数据库都支持“join-throughs”(Oracle)、层次 ID(SQL Server)或其他递归模式。另一种方法是使用基于集合的层次结构(通常依赖于触发器/维护)。在任何情况下,如果使用的 ORM 不能很好地支持递归查询,那么直接使用数据库功能可能会产生额外的“成本”——无论是在手动查询/视图生成方面,还是在触发器等管理方面。如果你不使用时髦的 ORM,或者只是使用 iBatis 等逻辑分隔符,那么这个问题甚至可能不适用。

就性能而言,在新的 Oracle 或 SQL Server(以及可能的其他)RDBMS 上,它应该非常具有可比性,所以这将是我最不担心的:但请查看适用于您的 RDBMS 和可移植性问题的解决方案。

于 2010-11-24T02:04:46.563 回答
0

每个建议您不要在数据库中引入层次结构的人,只考虑拥有自引用表的选项。这不是在数据库中对层次结构建模的唯一方法。您可以使用不同的方法,它为您提供更轻松、更快速的查询,而无需使用递归查询。假设您的层次结构中有一大组节点(类别):

Set1 = (Node1 Node2 Node3...)

这个集合中的任何节点也可以是另一个集合,它包含其他节点或嵌套集合:

Node1=(Node2 Node3=(Node4 Node5=(Node6) Node7))

现在,我们如何建模呢?让每个节点有两个属性,设置它包含的节点的边界:

节点 = { Id: int, Min: int, Max: int }

为了对我们的层次结构进行建模,我们只需相应地分配这些最小值/最大值:

节点 1 = { ID = 1,最小值 = 1,最大值 = 10 }
节点 2 = { ID = 2,最小值 = 2,最大值 = 2 }
节点 3 = { ID = 3,最小值 = 3,最大值 = 9 }
节点 4 = { ID = 4,Min = 4,Max = 4 }
Node5 = { Id = 5,Min = 5,Max = 7 }
Node6 = { Id = 6,Min = 6,Max = 6 }
Node7 = { Id = 7,Min = 8 , 最大值 = 8 }

现在,查询 Set/Node5 下的所有节点:

选择 n.* 从 Nodes as n, Nodes as s
where s.Id = 5 and s.Min < n.Min and n.Max < s.Max

唯一消耗资源的操作是如果要插入新节点或在层次结构中移动某些节点,因为许多记录会受到影响,但这很好,因为层次结构本身不会经常更改。

于 2013-02-28T19:50:44.713 回答