规范化数据库有什么好处?

What are the advantages of normalizing a database?

我真的无法很好地理解这一点。我在互联网上看到很多例子,但缺乏解释原因。例如,您在应用程序中有两种用户,客户和经理。创建单独的 "user_type" table 而不是将 "user_type" 列合并到用户 table 中有什么好处?另一个示例是为产品的类别创建单独的 table,而不是将类别字段放在产品 table 中。我真的无法理解这件事。数据库规范化的优点是什么?以及数据库中重复数据的缺点?

重复数据的缺点是您必须记住所有数据副本的位置,并在信息更改时更新所有副本。规范化更多的是关于重复数据删除而不是创建单独的 tables.

在您的 user_type 示例中,您可以将其放在用户 table 的列中。如果用户可以有多个用户类型,额外的 table 会派上用场。

规范化的既定目的是防止异常数据进入数据库。

例如,取一个非常简单的用户实体:

create table Users(
  ID        int auto_generated primary key,
  LastName  varchar( 16 ) not null,
  FirstName varchar( 16 ) not null
);

现在发现可以有两种类型的用户:Manager (M) 或 Peon (P)。此外,对于经理,我们需要知道有多少人直接向他们汇报,对于工薪阶层,我们需要知道他们是否有叉车执照。让我们把它扔进 table:

create table Users(
  ID           int auto_generated primary key,
  LastName     varchar( 16 ) not null,
  FirstName    varchar( 16 ) not null,
  UserType     char( 1 ) not null,
  DirectReps   int,
  HasFLLicense boolean
);

这应该会在您的脑海中响起各种警报。会出什么问题?

  • UserType 的唯一有效值(此时)是 'M' 或 'P'。但是,该字段可以包含任何单个字符。我们可以在此专栏上创建一个 "check",但这会产生另一个问题,我将在稍后介绍。
  • 仅当 UserType 字段分别包含 'M' 或 'P' 时,DirectReps 和 HasFLLicense 字段才适用。无法在内部强制执行此操作——必须使用触发器 and/or 应用程序代码来完成。

没有简单的方法来强制执行这些新约束。而且我们都知道,无论我们多么小心,我们迟早会在 DirectReps 或 HasFLLicense 中不恰当地使用 'M' 或 'P' and/or 值以外的 UserType 值。这是异常数据。

如果仔细观察,LastName、FirstName 和 UserType 字段仅对 ID 具有功能依赖性。但是,DirectReps 和 HasFLLicense 也依赖于 UserType。这个新的 table 不在 2nf.

规范化此 table 可以通过几种不同的方式完成,所以我不会深入讨论。 (Here 是我今天早些时候介绍的一种方法。)不用说,最终结果应该是这样的,除非 UserType 值为 'M',否则 DirectReps 值不存在,而 HasFLLicense 值不存在,除非用户类型值为 'P'.

现在让我们看看规范化的 table:

create table Users(
  ID           int auto_generated primary key,
  LastName     varchar( 16 ) not null,
  FirstName    varchar( 16 ) not null,
  UserType     char( 1 ) check( UserType in( 'M', 'P' )
);

有一些规范化方法也可以从 table 中删除 UserType 字段,但我们暂时将其保留在这里。无论位于何处,以下内容均适用。

当你想到迟早会需要第三种类型时,问题就出现了。在不同的时间点,一个接一个,你就明白了。每次我们都必须发出 alter table 命令来重写检查约束。这就像完全拆除和重建你的家只是为了更换你的客厅沙发。访问此 table 的所有代码(包括视图)都必须至少重新编译并可能重写。让我们面对现实吧,alter table 是一个不平凡的操作。

包含每个用户类型的记录的查找 table 有多好:

create table UserTypes(
  ID      char( 1 ) not null primary key, -- 'M' or 'P' or whatever
  Name    varchar( 16 ) not null,  -- "Manager" or "Peon" or whatever
  ...     -- other possible attributes
);

那么UserType字段可以定义为:

  UserType     char( 1 ) not null references UserTypes( ID )

现在添加类型 'A' 或 'F' 或其他任何类型,只需将记录插入 UserTypes table。现有代码不受影响。当然,最终必须更改应用程序代码以处理新类型,但应用程序开发人员现在可以在闲暇时进行此操作。

不要只针对当前需求进行设计。还针对合理预期的未来需求进行设计。