16

我发现这个问题很多,我不确定最好的方法。

我的问题是如何在使用外键查找表或直接在请求它的表中使用查找表值之间做出决定,完全避免查找表关系。

要记住的几点:

  • 使用第二种方法,如果在查找表中更改了数据,您将需要对引用数据的所有记录进行大规模更新。

  • 这更侧重于具有大量列引用许多查找表的表。因此,每次查询表时,大量外键意味着大量连接。

  • 该数据将来自从查找表中提取的下拉列表。为了在重新加载时匹配数据,值需要在现有列表中(与第一点相关)。

这里有最佳实践,还是需要考虑的关键点?

4

7 回答 7

29

您可以使用带有 VARCHAR 主键的查找表,并且您的主数据表在其列上使用 FOREIGN KEY,并进行级联更新。

CREATE TABLE ColorLookup (
  color VARCHAR(20) PRIMARY KEY
);

CREATE TABLE ItemsWithColors (
  ...other columns...,
  color VARCHAR(20),
  FOREIGN KEY (color) REFERENCES ColorLookup(color)
    ON UPDATE CASCADE ON DELETE SET NULL
);

该解决方案具有以下优点:

  • 您可以查询主数据表中的颜色名称,而无需连接到查找表。
  • 然而,颜色名称受限于查找表中的颜色集。
  • 您可以通过查询查找表来获取唯一颜色名称的列表(即使当前没有在主数据中使用)。
  • 如果您更改查找表中的颜色,则更改会自动级联到主数据表中的所有引用行。

令我惊讶的是,这个线程上的许多其他人似乎对什么是“规范化”有错误的想法。使用代理键(无处不在的“id”)与规范化无关!


来自@MacGruber 的重新评论:

是的,尺寸是一个因素。例如,在 InnoDB 中,每个二级索引都存储出现给定索引值的行的主键值。因此,您拥有的二级索引越多,对主键使用“庞大”数据类型的开销就越大。

这也会影响外键;外键列必须与其引用的主键具有相同的数据类型。您可能有一个小型查找表,因此您认为 50 行表中的主键大小无关紧要。但是该查找表可能会被其他表中的数百万或数十亿行引用!

没有适用于所有情况的正确答案。对于不同的情况,任何答案都可能是正确的。您只需了解权衡取舍,并尝试根据具体情况做出明智的决定。

于 2008-12-20T20:00:28.150 回答
5

在简单的原子值的情况下,我倾向于不同意这一点的普遍智慧,主要是在复杂性方面。考虑一张包含帽子的桌子。您可以执行“非规范化”方式:

CREATE TABLE Hat (
  hat_id INT NOT NULL PRIMARY KEY,
  brand VARCHAR(255) NOT NULL,
  size INT NOT NULL,
  color VARCHAR(30) NOT NULL /* color is a string, like "Red", "Blue" */
)

或者您可以通过制作“颜色”表来对其进行更多规范化:

CREATE TABLE Color (
  color_id INT NOT NULL PRIMARY KEY,
  color_name VARCHAR(30) NOT NULL
)

CREATE TABLE Hat (
  hat_id INT NOT NULL PRIMARY KEY,
  brand VARCHAR(255) NOT NULL,
  size INT NOT NULL,
  color_id INT NOT NULL REFERENCES Color(color_id)
)

后者的最终结果是您增加了一些复杂性 - 而不是:

SELECT * FROM Hat

你现在不得不说:

SELECT * FROM Hat H INNER JOIN Color C ON H.color_id = C.color_id

额外的加入是一笔大买卖吗?不——事实上,这是关系设计模型的基础——规范化可以防止数据中可能出现的不一致。但是像这样的每一种情况都会增加一点复杂性,除非有充分的理由,否则值得问问你为什么要这样做。我认为可能的“充分理由”包括:

  • 是否还有其他“挂断”该属性的属性?例如,您是否正在捕获“颜色名称”和“十六进制值”,使得十六进制值始终取决于颜色名称?如果是这样,那么您肯定需要一个单独的颜色表,以防止出现一行有(“Red”、“#FF0000”)而另一行有(“Red”、“#FF3333”)的情况。多个相关属性是实体应该被规范化的#1 信号。
  • 这组可能的值会经常变化吗?使用规范化查找表将使将来对集合元素的更改更容易,因为您只是更新单行。但是,如果这种情况不常见,请不要拒绝必须更新主表中大量行的语句;数据库在这方面做得很好。如果您不确定,请进行一些速度测试。
  • 这组可能的值是否会由用户直接管理?即是否有一个屏幕可以添加/删除/重新排序列表中的元素?如果是这样,显然必须有一个单独的表。
  • 不同值的列表是否会为某些 UI 元素提供支持?例如,“颜色”是 UI 中的下拉列表吗?那么你最好将它放在自己的表中,而不是每次需要显示下拉列表时都在表上执行 SELECT DISTINCT。

如果这些都不适用,我很难找到另一个(好的)理由来规范化。如果您只是想确保该值是某个(小)合法值集之一,则最好使用 CONSTRAINT 表示该值必须在特定列表中;让事情变得简单,如果需要,您可以随时“升级”到单独的表。

于 2008-12-20T15:46:36.167 回答
3

没有人考虑过的一件事是,如果查找表中的数据可以随时间变化并且加入的记录是历史记录,则您不会加入查找表。示例是零件表和订单表。供应商可能会丢弃零件或更改零件编号,但订单表应始终准确包含订购时订购的内容。因此,它应该查找数据以进行记录插入,但绝不应该加入查找表以获取有关现有订单的信息。相反,零件编号、描述和价格等应存储在订单表中。这一点尤其重要,这样价格变化就不会通过历史数据传播,并使您的财务记录不准确。在这种情况下,您还希望避免使用任何类型的级联更新。

于 2008-12-22T22:00:38.580 回答
2

rauhr.myopenid.com写道

我们决定解决这个问题的方法是使用第四范式。...

这不是第四范式。这是一个常见的错误,称为 One True Lookup: http ://www.dbazine.com/ofinterest/oi-articles/celko22

第四范式是: http ://en.wikipedia.org/wiki/Fourth_normal_form

于 2008-12-20T20:42:10.797 回答
1

规范化被普遍认为是数据库最佳实践的一部分,规范化说是的,你将数据推出并通过键引用它。

于 2008-12-20T06:45:33.933 回答
1

由于没有其他人解决您的第二点:当查询由于所有这些连接而变得冗长且难以读写时,视图通常会解决该问题。

于 2008-12-20T08:13:11.120 回答
1

您甚至可以制定一条规则,始终针对视图进行编程,让视图获取查找。

这使得优化视图和使您的代码能够抵抗表中的更改成为可能。

在 oracle 中,如果需要,您甚至可以将视图转换为物化视图。

于 2008-12-20T08:28:39.297 回答