数据库设计基础

设计正确的数据库使你能够访问最新、准确的信息。 由于正确的设计对于实现使用数据库的目标至关重要,因此投入时间来学习良好设计的原则是有意义的。 最后,你更有可能最终拥有一个满足需求且可以轻松适应更改的数据库。

本文提供规划桌面数据库的指南。 你将了解如何确定所需的信息、如何将该信息划分为相应的表和列,以及这些表彼此之间的关系。 在创建第一个桌面数据库之前,应先阅读本文。

本文内容

需要了解的一些数据库术语

Access 将信息整理到 表中:行列表和列,让人想起会计板或电子表格。 在简单数据库中,可能只有一个表。 对于大多数数据库,需要多个数据库。 例如,可能有一个表存储有关产品的信息,另一个表存储有关订单的信息,另一个表包含有关客户的信息。

描述数据表中三个表的图像

每行更正确地称为 记录,每列称为 字段。 记录是一种有意义且一致的方式,用于合并有关某些内容的信息。 字段是单个信息项 - 显示在每条记录中的项类型。 例如,“产品”表中的每行(即每条记录)包含有关某种产品的信息。 每列(即每个字段)包含该有关产品的某类信息,例如其名称或价格。

返回页首

什么是优秀数据库设计?

某些原则指导数据库设计过程。 第一个原则是,重复信息 (也称为冗余数据) 是错误的,因为它会浪费空间并增加错误和不一致的可能性。 第二个原则是信息的正确性和完整性非常重要。 如果数据库包含不正确的信息,则从数据库拉取信息的任何报表也将包含不正确的信息。 因此,你根据这些报告做出的任何决策都将受到错误信息。

因此,良好的数据库设计是:

  • 将信息划分为基于主题的表以减少冗余数据。

  • 为 Access 提供根据需要将表中的信息联接在一起所需的信息。

  • 帮助支持并确保信息的准确性和完整性。

  • 满足数据处理和报告需求。

返回页首

设计过程

设计过程包括以下步骤:

  • 确定数据库的用途    

    这有助于为剩余步骤做好准备。

  • 查找和组织所需的信息     

    收集你可能想要在数据库中记录的所有信息类型,例如产品名称和订单号。

  • 将信息划分为表    

    将信息项划分为主要实体或主题,例如产品或订单。 然后,每个主题将成为一个表。

  • 将信息项转换为列    

    确定要在每个表中存储哪些信息。 每个项都将成为一个字段,并在表中显示为一列。 例如,Employees 表可能包含“姓氏”和“雇用日期”等字段。

  • 指定主键    

    选择每个表的主键。 主键是用于唯一标识每行的列。 例如,产品 ID 或订单 ID。

  • 设置表关系    

    查看每个表并确定一个表中的数据与其他表中的数据之间的关系。 根据需要向表添加字段或创建新表以阐明关系。

  • 优化设计    

    分析设计中的错误。 创建表并添加一些示例数据记录。 查看是否可以从表获取所需的结果。 根据需要对设计进行调整。

  • 应用规范化规则    

    应用数据规范化规则以查看表结构是否正确。 根据需要对表进行调整。

返回页首

确定数据库的用途

最好在纸上写下数据库的用途,包括其用途、预期如何使用数据库以及谁将使用它。 例如,对于家庭业务的小型数据库,可以编写一些简单内容,例如“客户数据库保留客户信息列表,以便生成邮件和报告”。 如果数据库更复杂或由许多人使用(经常发生在公司设置中),则目的可能很容易是一段或更多段落,并且应包括每个人何时以及如何使用数据库。 其思路是创建一个可在整个设计过程中引用的完善任务声明。 当你做出决策时,拥有这样的声明可以帮助你专注于目标。

返回页首

查找和组织所需的信息

若要查找和组织所需的信息,请从现有信息开始。 例如,可以在账本中记录采购订单,或将客户信息保存在文件柜中的纸质表单上。 收集这些文档并列出 (显示的每种类型的信息,例如,在表单上填写的每一个框) 。 如果没有任何现有表单,请假设必须设计一个窗体来记录客户信息。 你会在表单上填写哪些信息? 你将创建哪些填充框? 标识并列出其中每个项。 例如,假设你当前将客户列表保留在索引卡上。 检查这些卡片可能会发现每个卡都有客户名称、地址、城市、州、邮政编码和电话号码。 其中每个项都表示表中的一个潜在列。

准备此列表时,一开始不必担心要使其完美无缺。 相反,请列出想到的每个项。 如果其他人将使用数据库,也询问他们的想法。 稍后可以微调列表。

接下来,请考虑可能需要从数据库生成的报表或邮件的类型。 例如,你可能希望产品销售报表按区域显示销售额,或者希望库存摘要报表显示产品库存水平。 你可能还希望生成表单,以发送给宣布销售活动或提供保费的客户。 在脑海中设计报表,并想象一下它的外观。 你会在报表上放置哪些信息? 列出每个项。 对表单和预期创建的任何其他报表执行相同的操作。

正在设想产品库存报表的人员

考虑可能想要创建的报表和邮件有助于确定数据库中所需的项。 例如,假设你让客户有机会选择加入 (或退出) 定期电子邮件更新,并且你希望打印已选择加入的用户的列表。 若要记录该信息,请向客户表添加“发送电子邮件”列。 对于每个客户,可以将字段设置为“是”或“否”。

向客户发送电子邮件的要求建议另一项记录。 一旦知道客户想要接收电子邮件,你还需要知道要向其发送电子邮件的电子邮件地址。 因此,你需要为每个客户记录一个电子邮件地址。

最好构建每个报表或输出列表的原型,并考虑生成报表需要哪些项。 例如,当你检查表格信时,可能会想到一些事情。 如果要包含适当的问候语(例如,启动问候语的“Mr.”、“Mrs.”或“Ms.”字符串),则必须创建一个问候语项。 此外,你通常可能以“亲爱的史密斯先生”而不是“亲爱的”开头写一封信。 西尔维斯特·史密斯先生。 这表明你通常需要将姓氏与名字分开存储。

需要记住的一个关键点是,应将每条信息分解为最小的有用部分。 对于名称,若要使姓氏随时可用,请将该名称分为两个部分: 名字和姓氏。 例如,按姓氏对报表进行排序,将客户的姓氏单独存储会有所帮助。 通常,如果要根据信息项进行排序、搜索、计算或报告,则应将该项目放在其自己的字段中。

请考虑你可能希望数据库回答的问题。 例如,你上个月关闭了多少个精选产品的销售额? 你最好的客户住在哪里? 谁是你的最畅销产品的供应商? 预测这些问题有助于专注于要记录的其他项目。

收集此信息后,即可开始下一步。

返回页首

将信息划分为表

若要将信息划分为表,请选择主要实体或主题。 例如,在查找和组织产品销售数据库的信息后,初步列表可能如下所示:

划分为若干主题的手写信息项

此处显示的主要实体是产品、供应商、客户和订单。 因此,从这四个表开始是有意义的:一个用于有关产品的事实,一个用于有关供应商的事实,一个用于有关客户的事实,一个用于有关订单的事实。 虽然这不会完成列表,但它是一个很好的起点。 可以继续优化此列表,直到设计效果良好。

首次查看项目初步列表时,可能会尝试将它们全部放在单个表中,而不是上图中显示的四个。 你将在这里了解为什么这是一个坏主意。 考虑一下,如下所示的表:

显示同时包含产品和供应商的表的图像

在这种情况下,每一行都包含有关产品及其供应商的信息。 由于可以有多个来自同一供应商的产品,因此必须多次重复供应商名称和地址信息。 这很浪费磁盘空间。 在单独的“供应商”表中只记录一次供应商信息,然后将该表链接到“产品”表,是一种更好的解决方案。

当需要修改有关供应商的信息时,此设计的第二个问题就会出现。 例如,假设需要更改供应商的地址。 由于它在多处出现,因此可能出现在某处更改了地址却忘记在其他位置进行更改的意外情况。 仅在一个位置记录供应商的地址可解决此问题。

设计数据库时,始终尝试只记录一次每个事实。 如果发现自己在多个位置(例如特定供应商的地址)重复相同的信息,请将该信息放在单独的表中。

最后,假设 Coho Winery 只提供一种产品,并且你想要删除该产品,但保留供应商名称和地址信息。 如何在不同时丢失供应商信息的情况下删除产品记录? 不可能。 由于每条记录都包含有关产品的事实以及有关供应商的事实,因此如果不删除另一条记录,则无法删除其中一条记录。 若要将这些事实分开,必须将一个表拆分为两个表:一个表用于产品信息,另一个表用于供应商信息。 删除产品记录应仅删除有关产品的事实,而不删除有关供应商的事实。

选择由表表示的主题后,该表中的列应仅存储有关该主题的事实。 例如,产品表应仅存储有关产品的事实。 因为供应商地址是有关供应商的事实,而不是有关产品的事实,所以它属于供应商表中。

返回页首

将信息项转换为列

若要确定表中的列,请确定需要跟踪有关表中记录的主题的信息。 例如,对于“客户”表,“名称”、“地址”、“城市-州-Zip”、“发送电子邮件”、“称呼”和“电子邮件地址”构成一个良好的列起始列表。 表中的每条记录都包含相同的列集,因此您可以存储每个记录的名称、地址、城市-州/自治区/县/自治区/地区 例如,地址列包含客户的地址。 每个记录包含有关一个客户的数据,地址字段包含该客户的地址。

确定每个表的初始列集后,可以进一步优化列。 例如,将客户名称存储为两个单独的列是有意义的:名字和姓氏,以便你可以仅对这些列进行排序、搜索和索引。 同样,地址实际上由五个单独的组件组成:地址、城市、州/自治区、邮政编码和国家/地区,并且将它们存储在单独的列中也有意义。 例如,如果要按状态执行搜索、筛选或排序操作,则需要将状态信息存储在单独的列中。

你还应考虑数据库是仅保存来自国内还是国际的信息。 例如,如果计划存储国际地址,最好使用“区域”列而不是“州”,因为此类列可以同时容纳国内州和其他国家/地区的区域。 同样,如果要存储国际地址,邮政编码比邮政编码更有意义。

以下列表显示了确定列的一些提示。

  • 不包括计算数据    

    在大多数情况下,不应将计算结果存储在表中。 相反,你可以让 Access 在想要查看结果时执行计算。 例如,假设有一个产品订单报表,该报表显示数据库中每个类别产品的订单单位小计。 但是,任何表中都没有“按订单单位”小计列。 相反,“产品”表包含一个“订单单位”列,用于存储每个产品的订单单位。 使用该数据,Access 会在每次打印报表时计算小计。 小计本身不应存储在表中。

  • 将信息存储在最小的逻辑部分    

    你可能倾向于将单个字段用于全名或产品名称以及产品说明。 如果在一个字段中组合了多种信息,以后很难检索单个事实。 尝试将信息分解为逻辑部分;例如,为名字和姓氏创建单独的字段,或者为产品名称、类别和说明创建单独的字段。

显示设计过程中的信息项的图像

优化每个表中的数据列后,即可选择每个表的主键。

返回页首

指定主键

每个表应包含一列或一组列,这些列唯一标识表中存储的每一行。 这通常是唯一的标识号,例如员工 ID 号或序列号。 在数据库术语中,此信息称为表 的主键 。 Access 使用主键字段快速关联多个表中的数据,并将数据汇集在一起。

如果已有表的唯一标识符(例如唯一标识目录中每个产品的产品代码),则可以使用该标识符作为表的主键,但前提是此列中的值对于每个记录始终不同。 主键中不能有重复值。 例如,不要使用人员姓名作为主键,因为名称不唯一。 你可以轻松地在同一个表中有两个同名的人。

主键必须始终具有值。 如果某个列的值可能变得未分配或未知 (缺失值) 在某个时刻,则它不能用作主键中的组件。

应始终选择其值不会更改的主键。 在使用多个表的数据库中,表的主键可用作其他表中的引用。 如果主键发生更改,还必须在引用密钥的所有位置应用更改。 使用不会更改的主键可降低主键与引用主键的其他表不同步的可能性。

通常,任意唯一数用作主键。 例如,可以为每个订单分配唯一的订单编号。 订单号的唯一用途是标识订单。 分配后,它永远不会更改。

如果你没有考虑到一列或一组列可能会成为一个好的主键,请考虑使用具有自动编号数据类型的列。 使用自动编号数据类型时,Access 会自动为你分配一个值。 这种标识符是无事实的:它不包含描述它所表示的行的事实信息。 无事实标识符非常适合用作主键,因为它们不会更改。 包含有关行(例如电话号码或客户名称)的主键更有可能更改,因为事实信息本身可能会更改。

显示具有主键字段的“产品”表的图像

1. 设置为“自动编号”数据类型的列通常使主键成为一个很好的主键。 没有两个产品 ID 相同。

在某些情况下,你可能希望使用两个或更多字段,这些字段共同提供表的主键。 例如,存储订单行项的“订单详细信息”表在其主键中使用两列:“订单 ID”和“产品 ID”。 主键采用多个列时,它也称为组合键。

对于产品销售数据库,可以为充当主键的每个表创建自动编号列:“产品”表的“ProductID”、“订单”表的“OrderID”、“客户”表的“CustomerID”和“供应商”表的“SupplierID”。

显示设计过程中的信息项的图像

返回页首

创建表关系

现在,你已将信息划分为多个表,你需要一种以有意义的方式再次将信息组合在一起的方法。 例如,以下表单包含来自多个表的信息。

“订单”窗体

1. 此窗体中的信息来自“客户”表...

2. ...Employees 表...

3. ...订单表...

4. ...“产品”表...

5. ...和“订单详细信息”表。

Access 是一种关系数据库管理系统。 在关系数据库中,可将信息划分为单独的基于主题的表。 然后,使用表关系根据需要将信息汇集在一起。

返回页首

创建一对多关系

请考虑此示例:产品订单数据库中的“供应商”和“产品”表。 供应商可以提供任意数量的产品。 因此,对于在“供应商”表中表示的任何供应商,“产品”表中可以表示许多产品。 因此,“供应商”表和“产品”表之间的关系是一对多关系。

一对多的概念

若要在数据库设计中表示一对多关系,请在关系“一”端获取主键,并将其作为附加列添加到关系“多”端的表中。 例如,在这种情况下,将“供应商 ID”列从“供应商”表添加到“产品”表中。 然后,Access 可以使用“产品”表中的供应商 ID 号查找每个产品的正确供应商。

“产品”表中的“供应商 ID”列称为外键。 外键是另一个表的主键。 “产品”表中的“供应商 ID”列是外键,因为它也是“供应商”表中的主键。

显示设计过程中的信息项的图像

通过建立主键和外键的配对,提供联接相关表的基础。 如果不确定哪些表应共享公共列,则确定一对多关系可确保所涉及的两个表确实需要共享列。

返回页首

创建多对多关系

请考虑 Products 表和 Orders 表之间的关系。

单个订单中可以包含多个产品。 另一方面,一个产品可能出现在多个订单中。 因此,对于“订单”表中的每条记录,都可能与“产品”表中的多条记录对应。 对于“产品”表中的每条记录,“订单”表中可以有许多记录。 这种类型的关系称为多对多关系,因为对于任何产品,都可以有许多订单:对于任何订单,都可以有许多产品。 请注意,若要检测表之间的多对多关系,请务必考虑关系的两端。

这两个表的主题(订单和产品)具有多对多关系。 这会带来问题。 若要了解此问题,请想象一下,如果尝试通过将“产品 ID”字段添加到 Orders 表来创建这两个表之间的关系,会发生什么情况。 若要每个订单有多个产品,每个订单的“订单”表中需要多个记录。 你将针对与单个订单相关的每一行重复订单信息,从而导致设计效率低下,从而导致数据不准确。 如果将“订单 ID”字段放在“产品”表中,则会遇到相同的问题 — 每个产品的“产品”表中会有多个记录。 如何解决此问题?

答案是创建第三个表(通常称为交接点表),将多对多关系分解为两个一对多关系。 将这两个表的主键都插入到第三个表中。 因此,第三个表记录关系的每个匹配项或实例。

多对多关系

“订单详细信息”表中的每条记录代表一个订单上的一个行项。 “订单详细信息”表的主键由两个字段组成:“订单”表和“产品”表中的外键。 单独使用“订单 ID”字段不能用作此表的主键,因为一个订单可以包含许多行项。 订单上的每个行项重复订单 ID,因此字段不包含唯一值。 单独使用“产品 ID”字段也不起作用,因为一个产品可以出现在许多不同的订单上。 但是,这两个字段在一起始终为每个记录生成唯一值。

在产品销售数据库中,“订单”表和“产品”表不直接相关。 相反,它们通过“订单详细信息”表间接关联。 订单和产品之间的多对多关系通过使用两个一对多关系在数据库中表示:

  • “订单”表和“订单详细信息”表具有一对多关系。 每个订单可以有多个行项,但每个订单项仅连接到一个订单。

  • “产品”表和“订单详细信息”表具有一对多关系。 每个产品可以有多个与其关联的行项,但每个行项仅引用一个产品。

从“订单详细信息”表中,可以确定特定订单上的所有产品。 还可以确定特定产品的所有订单。

合并“订单详细信息”表后,表和字段列表可能如下所示:

显示设计过程中的信息项的图像

返回页首

创建一对一关系

另一种类型的关系是一对一关系。 例如,假设需要记录一些特殊补充产品信息,这些信息很少需要,或者仅适用于少数产品。 由于不经常需要信息,并且因为将信息存储在 Products 表中会导致它不适用于的每个产品都留空,因此请将其放在单独的表中。 与 Products 表一样,使用 ProductID 作为主键。 此补充表与 Product 表之间的关系是一对一关系。 对于 Product 表中的每条记录,补充表中存在一条匹配记录。 标识此类关系时,这两个表必须共享一个公共字段。

检测到数据库中需要一对一关系时,请考虑是否可以将两个表中的信息放在一个表中。 如果出于某种原因而不想执行此操作,可能是因为它会导致大量空白,以下列表显示了如何在设计中表示关系:

  • 如果两个表具有相同的主题,则可以通过在两个表中使用相同的主键来设置关系。

  • 如果两个表具有不同主键的不同主题,请选择其中一个表 (其中一个) ,并将其主键作为外键插入另一个表中。

确定表之间的关系有助于确保具有正确的表和列。 当存在一对一或一对多关系时,涉及的表需要共享一个或多个公共列。 存在多对多关系时,需要第三个表来表示关系。

返回页首

优化设计

拥有所需的表、字段和关系后,应使用示例数据创建和填充表,并尝试使用信息:创建查询、添加新记录等。 这样做有助于突出显示潜在问题,例如,可能需要添加在设计阶段忘记插入的列,或者可能有一个表,应拆分为两个表以删除重复项。

查看是否可以使用数据库来获取所需的答案。 创建表单和报表的粗略草稿,并查看它们是否显示了所需的数据。 查找不必要的重复数据,并在找到任何数据时更改设计以消除它。

尝试初始数据库时,可能会发现改进的余地。 以下是要检查的一些事项:

  • 是否忘记了任何列? 如果是,则信息是否属于现有表中? 如果它是有关其他内容的信息,则可能需要创建另一个表。 为需要跟踪的每个信息项创建一列。 如果无法从其他列计算信息,则可能需要一个新列。

  • 是否不需要任何列,因为它们可以从现有字段进行计算? 如果可以从其他现有列(例如,从零售价格计算的折扣价格)计算信息项,则通常最好这样做,并避免创建新列。

  • 是否在某个表中重复输入重复信息? 如果是这样,可能需要将表划分为具有一对多关系的两个表。

  • 是否有包含多个字段、有限数量的记录以及单个记录中的多个空字段的表? 如果是这样,请考虑重新设计表,使其具有更少的字段和更多的记录。

  • 每个信息项是否已分解为最小的有用部分? 如果需要对信息项进行报告、排序、搜索或计算,请将该项目放在其自己的列中。

  • 每列是否包含有关表主题的事实? 如果列不包含有关表的主题的信息,则它属于其他表。

  • 表之间的所有关系都是由公用字段表示的还是由第三个表表示的? 一对一和一对多关系需要通用列。 多对多关系需要第三个表。

优化“产品”表

假设产品销售数据库中的每一种产品属于一般类别,例如饮料、调味品或海鲜。 “产品”表可以包含一个字段,用于显示每个产品的类别。

假设在检查和优化数据库设计后,决定存储类别的说明及其名称。 如果将“类别说明”字段添加到“产品”表,则必须对属于该类别的每个产品重复每个类别说明, 这不是一个很好的解决方案。

更好的解决方案是使用数据库自己的表和主键,使类别成为数据库要跟踪的新主题。 然后,可以将“类别”表中的主键作为外键添加到“产品”表中。

“类别”和“产品”表具有一对多关系:一个类别可以包含多个产品,但一个产品只能属于一个类别。

查看表结构时,请注意重复组。 例如,假设有一个包含以下列的表:

  • 产品 ID

  • 名称

  • 产品 ID1

  • Name1

  • 产品 ID2

  • Name2

  • 产品 ID3

  • Name3

在这里,每个产品都是一组重复的列,仅通过在列名称的末尾添加一个数字来不同于其他列。 当看到以这种方式编号的列时,应重新访问设计。

这种设计有几个缺陷。 对于初学者,它强制你对产品数量设置上限。 超过该限制后,必须立即向表结构添加一组新的列,这是一项重大的管理任务。

另一个问题是,那些产品数量少于最大数量的供应商将浪费一些空间,因为额外的列将是空白的。 此类设计最严重的缺陷是,它使许多任务难以执行,例如按产品 ID 或名称对表进行排序或编制索引。

每当看到重复的组仔细查看设计时,将表拆分为两个。 在上面的示例中,最好使用两个表,一个用于供应商,一个用于产品,按供应商 ID 链接。

返回页首

应用规范化规则

可以将数据规范化规则 (有时只是称为规范化规则,) 作为设计中的下一步。 使用这些规则来查看表结构是否正确。 将规则应用于数据库设计的过程称为规范化数据库,或只是规范化。

在表示所有信息项并完成初步设计后,规范化将非常有用。 其思路是帮助你确保已将信息项划分为相应的表。 规范化不能做的是确保所有正确的数据项都以开头。

在每个步骤中,你都会连续应用规则,确保设计达到所谓的“正常形式”之一。 五种正常形式被广泛接受 - 第一种正常形式到第五种正常形式。 本文对前三个进行了扩展,因为它们是大多数数据库设计所必需的。

第一范式

第一个普通形式指出,在该表中的每一行和列交集处,都存在一个值,并且从不存在值列表。 例如,不能有一个名为 Price 的字段,在其中放置多个 Price。 如果将行和列的每个交集视为一个单元格,则每个单元格只能包含一个值。

第二范式

第二个普通形式要求每个非键列完全依赖于整个主键,而不仅仅是部分键。 如果主键包含多个列,则此规则适用。 例如,假设有一个表包含以下列,其中“订单 ID”和“产品 ID”构成主键:

  • 主键) (订单 ID

  • 产品 ID (主键)

  • 产品名称

此设计违反了第二种普通形式,因为产品名称依赖于产品 ID,但不依赖于订单 ID,因此它不依赖于整个主键。 必须从表中删除“产品名称”。 它属于其他表 (Products) 。

第三范式

第三种常规形式要求每个非键列不仅依赖于整个主键,而且要求非键列彼此独立。

另一种说法是,每个非键列都必须依赖于主键,而只依赖于主键。 例如,假设有一个包含以下列的表:

  • ProductID (主键)

  • 名称

  • SRP

  • Discount

假设折扣取决于 SRP) (建议的零售价。 此表违反了第三个普通格式,因为非键列 Discount 依赖于另一个非键列 SRP。 列独立意味着应该能够更改任何非键列,而不会影响任何其他列。 如果更改 SRP 字段中的值,折扣将相应地更改,从而违反该规则。 在这种情况下,“折扣”应移动到 SRP 上键的另一个表。

返回页首

此信息是否有帮助?

为保护您的隐私,请不要在您的反馈意见中包含联系信息。 查看我们的 隐私策略