2

我有一张表,其中存储了近 1000 万行的人的信息。

目前 State 是 person 表上的一个 char(2) 字段。正如您所料,这会导致大量数据重复。如果我将 State 数据规范化到它自己的表中并在 person 表中为其创建一个 FK 会导致更快的查询时间吗?

前:

SELECT Name, City, State FROM Person WHERE State = 'WI'

后:

SELECT p.Name, p.City, s.Name as State
FROM Person p
    INNER JOIN State s ON p.State == s.Id
WHERE s.Name = 'WI'

在我看来,这会提高性能,但在优化查询方面我远非专家。

4

6 回答 6

2

规范化可能会导致性能下降,但在这种情况下它很少会提高您的性能,因为现在服务器必须查看磁盘上的两个位置,而不仅仅是一个。

标准化有两个目的:

  1. 减少存储在磁盘上的数据量
  2. 允许在一个地方更新数据

您的查询不会受益于这些优势中的任何一个,因为

  1. char(2) 和 int (外键)之间没有太大区别
  2. 状态的两位数代码永远不会改变,因此您永远不需要更新它
于 2012-10-05T13:10:29.867 回答
1

如果您只谈论 2 个字符 - 那么将其拆分到新表可能没有多大用处。

(但是 - 考虑到允许的特定值域 - 如果有人输入 VX 或其他一些不允许的值怎么办 - 非规范化时无法正确或有效地进行约束)

如果您正在谈论其他信息,例如美国邮政缩写(2 个字符)和州全名,也许还有其他一些信息,那么绝对可以 - 将其分开。

作为实践问题,您应该始终在适当的规范化方面犯错(imo) - 然后经过冗长的争论之后,您才应该考虑非规范化。

于 2012-10-05T13:15:11.600 回答
1

如果您在状态表上创建一个非常窄的键,例如 TINYINT,那么您可以提高性能,但不能保证。不过,这完全值得测试。

考虑复制每个表,正确索引它们,然后使用分析器同时对两者运行查询。

您最终可能会提高 1% 的速度。

尽管如此,标准化很少是一个坏主意......

于 2012-10-05T13:15:44.373 回答
0

只是没有多少空间可以节省。
您可以使用 byte (tinyint) 作为状态表上的主键,因为只有 50 个状态。
Char(2) 是两个字节。
因此,您只能在 person 表中每行保存一个字节。

压缩数据的优点是更少的磁盘空间和更少的内存。
对于固定数量的内存,如果数据较小,则内存中数据的机会更高。

我认为 1 字节的大小差异不值得加入的开销。

但我会规范化只是因为这是一个很好的做法。
为什么让某人进入JZ状态?

第 3 范式失败
达到 3NF
如果有重复的组,则将这些组分离成它们自己的关系。

您可以使用 char(2) 作为状态表中的 PK,因此 char(2) 与 char(2) 直接匹配。
这将满足第 3 范式,因为值仅限于 50 个状态。
在这种情况下,您不需要加入选择,因为实际值在主表中。
在插入或更新时,强制执行 FK 关系,因此它必须是有效状态。

如果您想报告整个状态,您可以在状态表中添加一列以获取全名。

于 2012-10-05T13:22:00.297 回答
0

好吧..在您的查询中,您正在做一些非最佳的事情。

SELECT p.Name, p.City, s.Name as State
FROM   Person p
       INNER JOIN State s ON p.State == s.Id
WHERE s.Name = 'WI'

您将获得所有状态下记录的所有人员详细信息,然后您将使用“WI”过滤状态..

如果您尝试这样做,您将获得更少的状态!

SELECT p.Name, p.City, s.Name as State
FROM   Person p
       INNER JOIN State s 
         ON  p.State == s.Id
         and s.Name = 'WI'

为什么?因为您只会获得名称为 WI 而不是所有状态列的状态列。

之后..如果它适合您的索引,请在 name = 'WI' 的状态上创建一个过滤索引

那会帮助一些..

于 2012-10-06T18:59:34.677 回答
-2

搜索字符串索引(理论上)比搜索 int 索引慢得多。这意味着是的;规范化您的数据会使其更快。在实践中,我经常看到差异很小。YMMV。您可能需要预先缓存或单独选择状态 ID:

SELECT p.Name, p.City, s.Name as State
FROM Person p
INNER JOIN State s ON p.State == s.Id
WHERE s.Id = (select id from State where State.Name = 'WI');

与查询一样,最好进行测试和优化。

于 2012-10-05T13:17:14.807 回答