Skip to content

Latest commit

 

History

History
325 lines (209 loc) · 31 KB

数据库数据建模分库分表.md

File metadata and controls

325 lines (209 loc) · 31 KB

数据库分库分表

  1. 一开始系统使用一个物理机(服务器)存储多个 MySQL 业务数据库(与 Table 同一级别概念,是一个 MySQL 程序里的多个 库 Databases,比如订单库、用户库、促销库等等)。
  2. 主从数据库(读写分离)。
  3. 若即使已经读写分离的情况下单个物理机也开始不堪重负,则可以使用多个物理机,每个物理机存储一个 MySQL 业务数据库(比如订单库)。然后这几个物理机组成完整的数据集(数据一致),这群物理机称之为一个 MySQL 节点。
  4. 若单个物理机即使只负责单个业务数据库也不堪重负时,比如用户海量时对订单库的请求特别频繁。
    1. 则可以用单个 MySQL 节点(多个物理机)负责单个业务数据库。
    2. 然后若是单个库还是太大,这时要看是因为表多而导致数据多,还是因为单张表里面的数据多。如果是因为表多而数据多,使用垂直切分,根据业务切分成不同的库并分离到不同节点。如果是因为单张表的数据量太大,这时要用水平切分,即把表的数据按某种规则切分成多张表,甚至多个库上的多张表,即后面第 5 步的分表。
    3. 分库分表的顺序应该是先垂直分,后水平分。因为垂直分更简单,更符合人们处理现实世界问题的方式。
  5. 分表(比如哈希分表)。
  • 分库(DB Sharding / DB Partition)
    • 优点是更多服务器资源(计算、存储等)服务同一个库(系统扩展性、成本效益优化)
  • 分表(Table Sharding / Table Partition)
    • 优点是可以更好地并发读写表(比如单机或同一个进程里多线程查询或写入)

Sharding 方式

读写分离示意图




更多参考:https://www.itread01.com/content/1545433947.html

多主架构

分库分表后面临的问题

  • 事务支持 - 分库分表后,就成了分布式事务了。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。
  • 多库结果集合并(group by,order by)
  • 跨库 join - 执行分库分表之后,原本逻辑关联性很强的数据划分到不同的表、不同的库上,表的关联操作将受到限制,无法 join 位于不同分库的表,也无法 join 分表粒度不同的表,结果原本一次查询能够完成的业务,可能需要多次查询才能完成。粗略的解决方法:
    • 全局表:基础数据,所有库都拷贝一份。
    • 字段冗余:这样有些字段就不用 join 去查询了。
    • 系统层组装:分别查询出所有,然后组装起来,较复杂。
  • 额外的数据管理负担和数据运算压力 - 额外的数据管理负担,最为常见的是数据的定位问题和数据的增删改查的重复执行问题,这些都可以通过应用程序来解决,但必然会引起额外的逻辑运算。

在数据库分库分表的情况下,应用程序通常需要一种机制来确定应该将查询发送到哪个数据库分片或表。这通常不是应用自身内部存储的地址,而是通过以下方式之一实现的(by ChatGPT):

  • 中间件路由:使用专门的中间件或数据库代理来处理路由。这些中间件可以根据查询的特征,例如表的键范围或散列,将查询路由到正确的数据库分片或表。一些流行的数据库分片中间件包括 Vitess(用于 MySQL)和 Citus(用于 PostgreSQL)。
  • 数据库内置路由:有些数据库管理系统具有内置的分片支持,可以自动处理查询的路由。例如,Cassandra 和 MongoDB 等 NoSQL 数据库提供了内置的分片功能,它们可以根据分片键将查询路由到正确的分片。

它们又可以叫数据分区器(Data Partitioner)。而且数据分区器或数据库中间件路由器通常也需要冗余措施以防止单点故障。

参考:
https://www.youtube.com/watch?v=pn2j88PEW0Y
https://juejin.cn/post/6844903648670007310
https://www.bilibili.com/video/BV1yv41187tV?p=4
https://www.bilibili.com/video/BV1yv41187tV?p=5

数据模型设计

https://support.microsoft.com/en-us/office/database-design-basics-eb2159cf-1e30-401a-8084-bd4f9c9ca1f5
https://support.microsoft.com/zh-cn/office/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1%E5%9F%BA%E7%A1%80-eb2159cf-1e30-401a-8084-bd4f9c9ca1f5

数据库设计基础
通过设计适当的数据库,可以访问最新的准确信息。 由于正确的设计对于实现使用数据库的目标至关重要,因此投入所需时间来了解良好设计的原则是有意义的。 最后,最终很可能有一个符合需求且可轻松适应更改的数据库。

本文提供有关规划桌面数据库的指南。 您将了解如何确定需要哪些信息、如何将该信息划分为相应的表和列,以及这些表之间的关联方式。 创建第一个桌面数据库之前,应阅读本文。

重要: Access 提供设计体验,让你为 Web 创建数据库应用程序。 设计 Web 时,许多设计注意事项不同。本文不讨论 Web 数据库应用程序设计。有关详细信息,请参阅 "生成数据库以在 Web 上共享" 一文。

需要知道的一些数据库术语

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


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

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

什么是优秀数据库设计?

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

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

  • 将信息划分为基于主题的表,以减少冗余数据。
  • 根据需要为 Access 提供将表中的信息联接在一起所需的信息。
  • 帮助支持并确保信息的准确性和完整性。
  • 满足数据处理和报告需求。

设计过程

设计过程包括以下步骤:

  • 确定数据库的用途 - 这有助于为其余步骤做好准备。
  • 查找和组织所需的信息 - 收集可能要在数据库中记录的所有类型信息,例如产品名称和订单编号。
  • 将信息划分为表 - 将信息项划分为主要实体或主题,例如"产品"或"订单"。然后,每个主题将成为一个表。
  • 将信息项转换为列 - 确定要存储在每个表中的信息。每个项目将成为一个字段,在表中显示为列。例如,"员工"表可能包含"姓氏"和"聘用日期"等字段。
  • 指定主键 - 选择每个表的主键。主键是一个列,用于唯一标识每一行。例如,产品 ID 或订单 ID。
  • 设置表关系 - 查看每个表,确定一个表中的数据如何与其他表中的数据相关。根据需要向表添加字段或创建新表以阐明关系。
  • 优化设计 - 分析设计中的错误。创建表并添加示例数据的一些记录。查看能否从表中获取想要的结果。根据需要调整设计。
  • 应用规范化规则 - 应用数据规范化规则,查看表的结构是否正确。根据需要对表进行调整。

确定数据库的用途

建议在纸上写下数据库的用途-用途、预期使用方法以及使用对象。 例如,对于家庭型企业的小数据库,可以编写一些简单的内容,例如"客户数据库保留客户信息列表,以便生成邮件和报表"。 如果数据库更复杂或由许多人使用(如公司设置中经常出现的情况一样)则用途可能很容易是一段或多段内容,并且应包括每个人使用数据库的时间和方式。 其思路是制定一个开发良好的任务说明,在整个设计过程中都可以引用该声明。 制定此类声明有助于在做出决策时专注于目标。

查找和组织所需信息

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

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

接下来,考虑可能要从数据库生成的报告或邮件类型。例如,您可能希望产品销售报表按区域显示销售额,或显示产品库存级别的库存摘要报表。您可能还希望生成表单信函,以发送给宣布促销活动或提供高级产品的客户。在心中设计报表,想象一下它的外观。您将在报表上放置哪些信息?列出每个项目。对信函以及预期要创建的其他任何报表执行相同的更改。


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

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

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

合理构造每个报表或输出列表的原型,并考虑生成报表所需的项目。例如,检查表单信函时,可能会考虑一些问题。如果要包含适当的称呼,例如,启动问候语的"Mr."、"Mrs."或"Ms."字符串,必须创建称词项。此外,您通常以"尊敬的 Smith 先生"而不是"尊敬的 Sylvester Smith 先生"。这表示通常需要将姓氏与名字分开存储。

要记住的一个关键点是,应该将每条信息分解为最小的有用部分。对于名字,若要使姓氏随时可用,需要将姓氏分为两部分:名字和姓氏。例如,若要按姓氏对报表进行排序,可以单独存储客户的姓氏。一般情况下,如果要基于某个信息项进行排序、搜索、计算或报告,应该将该项目放入其自己的字段中。

考虑希望数据库回答的问题。例如,上个月你关闭的精选产品销售额有多少?最佳客户在哪里生活?你的最畅销产品的供应商是谁?预期这些问题可帮助你无需记录其他项目。

收集此信息后,即可执行下一步。

将信息分割成表

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


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

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

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


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

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

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

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

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

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

将信息项转换为列

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

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

此外,还应考虑数据库是否保存仅来自国内或国际的信息。例如,如果计划存储国际地址,最好使用"区域"列而不是"州"列,因为此类列可以同时容纳国内州以及其他国家/地区的区域。同样,如果打算存储国际地址,则 Postal Code 比 Zip Code 更有意义。

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

  • 不包括计算数据 - 在大多数情况下,不应将计算结果存储在表中。相反,当想要查看结果时,可以让 Access 执行计算。例如,假设有一份"订单产品"报表,其中显示数据库中每个类别产品订单单位的分类汇总。但是,任何表中都不存在"订单单位"小计列。相反,"产品"表包含一个"订单单位"列,用于存储每个产品的订单单位。使用该数据,Access 将每次打印报表时计算小计。小计本身不应存储在表中。
  • 将信息存储在最小的逻辑部分 - 你可能想要为全名或产品名称以及产品说明使用单个字段。如果在一个字段中合并了多类信息,则以后很难检索单个事实。尝试将信息分解为逻辑部分;例如,为名字和姓氏或者产品名称、类别和说明创建单独的字段。


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

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

指定主键

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

如果已有一个表的唯一标识符,例如唯一标识目录中每个产品的产品编号,可以使用该标识符作为表的主键,但只有当此列中的值对于每个记录始终不同时。主键中不能有重复值。例如,请勿将人员姓名用作主键,因为姓名不唯一。在同一个表中可以轻松地有两个同名的人。

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

应始终选择其值不会更改的主键。在使用多个表的数据库中,表的主键可以用作其他表中的引用。如果主键发生更改,还必须在引用密钥的任何地方应用更改。使用不会更改的主键可以减少主键与引用它的其他表不同步的可能性。

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

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


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

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

在某些情况下,可能需要使用两个或多个字段,它们共同提供表的主键。例如,存储订单明细项目的"订单明细"表将在其主键中使用两列:订单 ID 和产品 ID。主键采用多个列时,它也称为组合键。

对于产品销售数据库,可为每个表创建自动编号列作为主键:"产品"表的 ProductID、"订单"表的"订单 ID"、"客户"表的"客户 ID"和"供应商"表的"供应商 ID"。


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

创建表关系

将信息划分到表中后,需要一种方法以有意义的方式再次将信息汇集在一起。例如,以下窗体包含来自多个表的信息。


“订单”窗体

  1. 此窗体中的信息来自“客户”表...
  2. ..."员工"表...
  3. ..."订单"表...
  4. ..."产品"表...
  5. ...和"订单详细信息"表。

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

创建一对多关系

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


一对多的概念

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

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


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

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

创建多对多关系

请考虑"产品"表与"订单"表之间的关系。

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

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

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


多对多关系

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

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

  • "订单"表和"订单明细"表具有一对多关系。每个订单可以具有多个行项,但每个行项仅连接到一个订单。
  • "产品"表和"订单明细"表具有一对多关系。每个产品可以有许多与之关联的行项,但每个行项仅引用一个产品。

从"订单明细"表中,您可以确定特定订单上的所有产品。还可以确定特定产品的所有订单。

合并"订单明细"表后,表和字段的列表可能如下所示:


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

创建一对一关系

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

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

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

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

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

优化设计

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

查看能否使用数据库获取想要的答案。创建表单和报表的草稿,并查看它们是否显示预期的数据。查找不必要的重复数据,如果发现任何重复数据,请更改设计以消除重复数据。

试用初始数据库时,可能会发现改进空间。下面是一些需要检查的项:

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

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

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

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

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

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

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

优化"产品"表

假设产品销售数据库中的每个产品属于一般类别,例如饮料、食品或饮料。"产品"表可能包含一个显示每个产品的类别的字段。

假设在检查并优化数据库的设计后,你决定存储类别的说明及其名称。如果向"产品"表添加"类别说明"字段,必须针对该类别下的每个产品重复每个类别说明,这不是一个很好的解决方案。

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

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

查看表结构时,请查找重复的组。例如,考虑包含以下列的表:

  • 产品 ID
  • 姓名
  • 产品 ID1
  • Name1
  • 产品 ID2
  • Name2
  • 产品 ID3
  • Name3

此处,每个产品都是一组重复的列,它们仅通过向列名称的末尾添加数字而不同于其他列。看到采用这种方式编号的列时,应重新访问设计。

这种设计有几个缺陷。对于初学者,它会强制对产品数量设置上限。一旦超过该限制,必须将一组新的列添加到表结构,这是一个主要的管理任务。

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

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

应用规范化规则

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

在表示所有信息项并到达初步设计后,规范化最有用。其思路是帮助你确保将信息项划分到相应的表中。规范化不能做的是确保一开始具有所有正确的数据项。

在每个步骤中连续应用规则,确保设计到达所谓的"范式"之一。广泛接受五种范式 - 第一范式,通过第五种正常形式。本文对前三篇文章进行扩展,因为它们是大多数数据库设计所需的全部内容。

第一范式
第一种普通形式表示,在表中每一行和每列的交集处,存在单个值,永远不会存在值列表。例如,不能有一个名为 Price 的字段,其中你放置了多个价格。如果将行和列的每个交集视为单元格,则每个单元格只能包含一个值。

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

  • 订单 ID (主键)
  • 产品 ID (主键)
  • 产品名称

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

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

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

  • ProductID (主键)
  • 姓名
  • SRP
  • Discount

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