2

这是我最近一直在做的事情,想知道其他人是否也这样做,如果是,这种做法的名称是什么。

我在我的表中保留了快捷方式列,这样我就可以避免进行太多的连接。例如,如果我有一个用户表和一个地理表:

用户:

id | username | zip       | copy_latitude | copy_longitude | other info
------------------------------------------------------------------------
1  | Bob      | 11345     | 40.81518000   | -73.04550000   | etc...

地理:

id | zip_code | latitude    | longitude
----------------------------------------
1  | 11345    | 40.81518000 | -73.04550000

现在,如果我想获取 Bob 的纬度和经度,我可以在我用来获取 bob 的其他信息的同一个 select 语句中这样做:

SELECT a_bunch_of_other_columns, copy_latitude, copy_longitude 
FROM users WHERE id = 1;

vs(如果我没有保留快捷方式):

SELECT a_bunch_of_other_columns, latitude, longitude 
FROM users
INNER JOIN geo ON user.zip = geo.zip_code
WHERE users.id = 1;

通过保留快捷方式,我为自己节省了一个连接。现在,对于这个示例表和语句来说,这似乎没什么大不了的,但是我有一些带有 6 或 7 个连接的巨大表和语句,我觉得这很有帮助。

唯一的开销是在任何时候发生变化时都必须更新这两个地方。我通过存储过程来解决这个问题。

我的问题是:

  • 这是开发人员之间的常见做法吗?如果是这样,它叫什么?
  • 如果我这样做,我的数据库是否仍然正常化?(我会假设是这样,因为为了数据完整性,即使我不从那里使用它,我也总是将数据的有效副本保存在适当的位置)
4

4 回答 4

3

我不知道这是否普遍,但我确信这不是好的做法。每当您将数据存储在多个位置时,它都不是最佳的。当然,有时使用非规范化数据库来提高性能,例如报告数据库或数据仓库。在这些情况下,数据库通常是事务数据库的只读非规范化副本。

如果你真的需要减少你的连接,你不能只创建满足这些情况的视图吗?

您为提高查询性能而创建的所有这些重复数据当然会降低插入/更新性能。你如何跟踪所有这些额外的数据,如果它变得不同步会发生什么?当您离开而其他人必须发现所有需要更新数据的额外位置时会发生什么?

于 2012-05-09T03:57:30.553 回答
2

这是开发人员之间的常见做法吗?如果是这样,它叫什么?

我只能为自己说话——我不这样做

这是开发人员之间的常见做法吗?如果是这样,它叫什么?如果我这样做,我的数据库是否仍然正常化?(我会假设是这样,因为为了数据完整性,即使我不从那里使用它,我也总是将数据的有效副本保存在适当的位置)

顺便说一句 - 你有另一个开销 - 存储

于 2012-05-09T03:55:57.360 回答
2

它不再标准化,因为您的表中有重复的数据。

我想你可以称之为“非规范化”。

您真正会这样做的唯一一次是出于速度/优化目的,这就是您在问题中所说的,您已经这样做以消除复杂性。

老实说,在我的任何数据库中,我从来没有达到需要这样做以优化查询速度的地步。

我建议做一个基准测试,看看它比索引好的连接快多少

于 2012-05-09T04:03:32.923 回答
0

让我们这样看一下。你已经知道了一些。(PostgreSQL 语法;dbms 与规范化无关,仅用于实现。)

create table geo (
  zip_code char(5) not null,
  -- CHECK constraints on lat and long omitted.
  latitude float not null,
  longitude float not null,
  primary key (zip_code),
  unique (latitude, longitude)
);

create table users (
  user_id integer not null,
  username varchar(10) not null,
  zip_code char(5) not null, 
  primary key (user_id),
  foreign key (zip_code) references geo (zip_code) 
    on update cascade on delete restrict
);

很明显,这两个表都在 5NF 中。

可以为地理表创建一个 ID 号,并且可以将 users.zip_code 替换为该 ID 号。但是用代理ID号替换真实数据与规范化无关,它不会改变这些表的正常形式。

用 id 号替换真实数据确实会改变性能;每次需要用户的邮政编码时,都需要加入才能获得。这不是一个完全可预测的变化。实际性能因 dbms、服务器、密钥宽度等而异。你不应该在测试你自己的表时遇到麻烦。您可能会发现,在多达几百万行的情况下,自然键的性能优于代理 ID 号。(这就是我在这里为我们的生产数据库测试设计时发现的。)

现在让我们稍微改变一下结构。

create table geo (
  zip_code char(5) not null,
  -- CHECK constraints on lat and long omitted.
  latitude float not null,
  longitude float not null,
  primary key (zip_code),
  unique (latitude, longitude),
  -- Allows all three columns to be the target for a foreign key.
  unique (zip_code, latitude, longitude)
);

create table users (
  user_id integer not null,
  username varchar(10) not null,
  zip_code char(5) not null, 
  latitude float not null,
  longitude float not null,
  primary key (user_id),
  -- This FK has to reference all three columns. If split, it's possible
  -- to reference the latitude and longitude for the wrong zip code.
  foreign key (zip_code, latitude, longitude) 
    references geo (zip_code, latitude, longitude) 
    on update cascade on delete restrict
);

尽管此更改确实引入了传递依赖,但在 user_id -> zip_code 和 zip_code -> latitude 等中,它不会导致任何插入、更新或删除异常。这是因为传递依赖关系中涉及的所有值都由对 5NF 表的单个外键引用覆盖。

表geo还在5NF;用户现在处于 2NF。我们在这里得到或失去了什么?

  • 我们失去了磁盘空间来存储更广泛的外键数据和索引。
  • 多达一定数量的行(可能是几百万),我们在 SELECT 查询上获得更快的性能。(我没有测试你的模式,因为我没有时间。但我已经通过使用自然键测量了 20 到 30 倍的速度增加。你的差异可能不会那么显着。)
  • 我们在 INSERT 语句和大多数 UPDATE 语句上得到较慢的性能。(并不意味着。5msec 比 3msec 慢,但 5msec 不一定慢。我自己的大多数插入和更新都在几分之一毫秒内运行。)

所以构建一个测试模式,用几百万行填充它,然后测量性能。测试性能

  • zip_code 上的外键,以及获取纬度和经度的连接,然后
  • 使用 {zip_code, latitude, longitude} 上的外键重组和测试,然后
  • 使用代理 ID 号和连接进行重组和测试以获取 zip_code、纬度和经度。

并在此处发布结果。我很想见到他们。

于 2012-05-09T13:20:50.790 回答