返回

关系型数据库设计三大范式的突破

后端

关系型数据库设计:三大范式指南

在关系型数据库设计中,三大范式是指导原则,旨在确保数据完整性和一致性。这些范式分别是第一范式(1NF)、第二范式(2NF)和第三范式(3NF)。本文将深入探讨这些范式,重点阐述它们的意义、重要性和在实践中的应用。

第一范式(1NF)

想像一下你正在组织一本书架,里面有各种各样的小说和非小说类书籍。如果每本书都被塞进一个大箱子里,箱子里同时装着多个不同的书名,那么要找到一本特定的书就非常困难。同理,在数据库中,第一范式(1NF)要求表中的每一列都不可再分,这意味着每个字段都应该只存储一个原子值。

例如,一个违反 1NF 的表可能会这样:

CREATE TABLE customers (
  customer_id INT NOT NULL,
  name VARCHAR(255) NOT NULL,
  address VARCHAR(255) NOT NULL,
  phone VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL
);

在这个表中,address 字段包含了客户的街道地址、城市、州和邮政编码,这是违反 1NF 的,因为 address 字段是一个复合值,可以进一步分解为四个单独的列。

为了遵守 1NF,我们可以将 address 字段分解为四个单独的列:

CREATE TABLE customers (
  customer_id INT NOT NULL,
  name VARCHAR(255) NOT NULL,
  street_address VARCHAR(255) NOT NULL,
  city VARCHAR(255) NOT NULL,
  state VARCHAR(255) NOT NULL,
  zip_code VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL
);

第二范式(2NF)

想象你正在管理一家网上商店,你有一个名为 orders 的表,其中存储了客户订单信息。如果这个表包含每个客户的姓名、订单总额和订单日期,那么当客户更改姓名或地址时,你就需要更新这个表。这会造成数据冗余和不一致性的问题。

第二范式(2NF)要求表中的每一列都必须与表的主键相关。这意味着每个字段都应该存储与表的主键相关的信息。为了满足 2NF,我们需要将 orders 表分解为两个单独的表:

CREATE TABLE customers (
  customer_id INT NOT NULL,
  name VARCHAR(255) NOT NULL,
  address VARCHAR(255) NOT NULL,
  phone VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL
);

CREATE TABLE orders (
  order_id INT NOT NULL,
  customer_id INT NOT NULL,
  product_id INT NOT NULL,
  quantity INT NOT NULL,
  price DECIMAL(10, 2) NOT NULL
);

在这种情况下,customers 表的主键是 customer_id,而 orders 表的主键是 order_idorders 表中的每个字段都与表的主键 order_id 相关。

第三范式(3NF)

第三范式(3NF)要求表中的每一列都必须与表的主键或另一个与表的主键相关的列相关。这意味着每个字段都应该存储与表的主键或另一个与表的主键相关的列相关的信息。

一个违反 3NF 的表可能是这样的:

CREATE TABLE customers (
  customer_id INT NOT NULL,
  name VARCHAR(255) NOT NULL,
  address VARCHAR(255) NOT NULL,
  phone VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL,
  orders_count INT NOT NULL
);

在这个表中,orders_count 字段与表的主键 customer_id 相关,但它也与另一个与表的主键相关的列 orders 表相关。这违反了 3NF,因为 orders_count 字段应该存储在 orders 表中,该表的主键是 order_id

为了满足 3NF,我们可以将 customers 表分解为两个单独的表:

CREATE TABLE customers (
  customer_id INT NOT NULL,
  name VARCHAR(255) NOT NULL,
  address VARCHAR(255) NOT NULL,
  phone VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL
);

CREATE TABLE orders (
  order_id INT NOT NULL,
  customer_id INT NOT NULL,
  product_id INT NOT NULL,
  quantity INT NOT NULL,
  price DECIMAL(10, 2) NOT NULL
);

CREATE TABLE order_counts (
  customer_id INT NOT NULL,
  orders_count INT NOT NULL
);

在这种情况下,customers 表的主键是 customer_id,而 orders 表的主键是 order_idorder_counts 表跟踪每个客户的订单数量,它的主键是 customer_id,它与 customers 表的主键相关。

在实践中应用范式

在实践中,并不是所有的数据库表都需要满足 3NF。有时,为了提高性能或简化查询,可能需要使用反范式设计。例如,在我们的 customers 表中,我们可能希望存储 orders_count 字段,即使它违反了 3NF,以提高查询客户订单数量的效率。

然而,在使用反范式设计时,必须权衡利弊,并确保不会导致数据冗余或数据不一致。

常见问题解答

  1. 为什么数据库范式很重要?
    数据库范式有助于减少数据冗余、提高数据一致性和简化查询。

  2. 所有数据库表都需要满足 3NF 吗?
    不,在某些情况下,为了提高性能或简化查询,可以使用反范式设计。

  3. 违反数据库范式有什么后果?
    违反数据库范式会导致数据冗余、数据不一致和查询性能不佳。

  4. 如何确定一个表是否满足特定范式?
    你可以使用以下规则来检查一个表是否满足特定的范式:

    • 1NF:每个字段都应该只存储一个原子值。
    • 2NF:每个字段都应该与表的主键相关。
    • 3NF:每个字段都应该与表的主键或另一个与表的主键相关的列相关。
  5. 如何将一个表分解为多个表以满足数据库范式?
    你可以使用以下步骤将一个表分解为多个表以满足数据库范式:

    • 识别表的非主键列。
    • 对于每个非主键列,确定它是否与表的现有主键相关。
    • 如果一个非主键列与表的主键不相关,则将其移动到一个新表中。
    • 将新表的主键设置为非主键列。
    • 重复上述步骤,直到所有非主键列都与一个表的主键相关。