规范化数据库有什么好处?
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。现有代码不受影响。当然,最终必须更改应用程序代码以处理新类型,但应用程序开发人员现在可以在闲暇时进行此操作。
不要只针对当前需求进行设计。还针对合理预期的未来需求进行设计。
我真的无法很好地理解这一点。我在互联网上看到很多例子,但缺乏解释原因。例如,您在应用程序中有两种用户,客户和经理。创建单独的 "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。现有代码不受影响。当然,最终必须更改应用程序代码以处理新类型,但应用程序开发人员现在可以在闲暇时进行此操作。
不要只针对当前需求进行设计。还针对合理预期的未来需求进行设计。