一个表中或单独表中的三个客户地址?

时间:2008-11-22 08:15:59

标签: database language-agnostic database-design oop

在我的应用程序中,我有一个Customer类和一个Address类。 Customer类有Address类的三个实例:customerAddressdeliveryAddressinvoiceAddress

什么是在数据库中反映此结构的最佳方式?

  • 直截了当的方式是客户表和单独的地址表。
  • 更加非规范化的方式只是一个客户表,其中包含每个地址的列(“street”的示例:customer_street,delivery_street,invoice_street)

你有什么经历?这些方法有什么优点和缺点吗?

7 个答案:

答案 0 :(得分:11)

如果您100%确定客户将只拥有您描述的3个地址,那么这是可以的:

CREATE TABLE Customer
(
    ID int not null IDENTITY(1,1) PRIMARY KEY,
    Name varchar(60) not null,
    customerAddress int not null
        CONSTRAINT FK_Address1_AddressID FOREIGN KEY References Address(ID),
    deliveryAddress int null
            CONSTRAINT FK_Address2_AddressID FOREIGN KEY References Address(ID),
    invoiceAddress int null
            CONSTRAINT FK_Address3_AddressID FOREIGN KEY References Address(ID),
    -- etc
)

CREATE TABLE Address
(
    ID int not null IDENTITY(1,1) PRIMARY KEY,
    Street varchar(120) not null
    -- etc
)

否则我会像这样建模:

CREATE TABLE Customer
(
    ID int not null IDENTITY(1,1) PRIMARY KEY,
    Name varchar(60) not null
    -- etc
)

CREATE TABLE Address
(
    ID int not null IDENTITY(1,1) PRIMARY KEY,
    CustomerID int not null
        CONSTRAINT FK_Customer_CustomerID FOREIGN KEY References Customer(ID),
    Street varchar(120) not null,
    AddressType int not null 
    -- etc
)

答案 1 :(得分:5)

我会(就像数据库理论教导的那样)选择两个独立的表:客户和地址。

正如您所说,将三个字段放在Customer表中的想法很糟糕,因为它会违反规范化规则(并且当地址变为三个以上时会失败)。

编辑:另外,我将地址表记录分成几个字段,一个用于地名前缀,一个用于街道等,并在其中放置一个唯一的密钥。否则,你将以一个装满重复数据库的数据库结束。

答案 2 :(得分:4)

我会去非规范化。这更容易。

如果对其进行规范化,则地址表将要求您删除重复项并重新链接客户表中的记录。如果我有相同的交货和发票地址,它似乎应该链接到同一记录。如果我换一个,你需要:

  1. 创建新地址记录。
  2. 重新链接客户记录。
  3. 如果我将其更改回来,您需要检查:

    1. 是否存在类似的地址条目。
    2. 重新链接客户记录。
    3. 这种编程开销似乎消除了规范化似乎提供的更少空间的优势。像你指出的那样,非规范化解决方案可以提供更快的查询和更容易的维护(编程方式)。这似乎是我的决定性因素。

      如上所述,规范化解决方案将允许您稍后添加更多地址。但是你必须为外键添加一个字段,除非你计划组织表而没有从客户到地址表的链接。

      规范化的优点

      • 减少空间。
      • 内置重复逻辑(虽然他们可能不是实际重复?)
      • 支持添加新地址字段(有点)。

      非规范化的优点

      • 查询速度更快。
      • 少编程。

答案 3 :(得分:4)

使用2个表,Customer,Address。

在地址表中创建地址后,通常不允许修改地址(可能是修正拼写错误的特定工具)。 IOW使地址的ID与地址本身一致。

您现在可以在任何地方引用这些地址表条目。例如,当订单被分派给客户时,订单表引用的地址ID可以与客户表中的DeliveryAddressID字段中的地址ID相同。

如果客户希望将当前的文件传递地址更改为新地址,则会创建新的地址记录。历史交货数据不受此影响,但新订单会自动使用新地址。

注意这在缓存Addresss对象时也很有用(它们是不可变的并且对于长期缓存是安全的),它们可以分发并且更容易测试是否相等(通过ID属性)。

答案 4 :(得分:2)

我在Zen Cart中也有这个(也可能是osCommerce),在订单表中,想知道为什么他们这样做,甚至更多,因为他们有一个地址表。
我看到的唯一有效的原因是出于记录目的:即使客户更改了地址,订单信息也不得更改,它会反映订单时的数据。

现在,如果同一个客户做了很多订单,那就有点浪费了。一种可能的解决方案是保留地址历史记录并将其与订单一起引用 我想知道这些不可变的地址(或者如果它们还没有相关的订单可变的话)应保存在常规地址簿中或单独的历史表中,仅在订单生成时填写(如果同一客户保留,则避免重复)当然是相同的地址。) 前者的优点是避免使用两个非常相似的结构和重复信息的表,但随着历史的增长可能会阻碍性能(?)。虽然人们在实践中很少改变这些信息 后者具有分离角色的优势(一个是不可变的,而不是另一个),历史很少使用。

总的来说,如果您的应用程序不需要保留历史记录,只需选择两个单独的表。

答案 5 :(得分:2)

在这种情况下,将每个地址字段放在不同的行中不是标准化。它只是表分区。假设任何具有更多表的模式“更加规范化”都是错误的。

假设我们在数据库中有这两种替代模式: 1)user:user_id,username,password

2)user:user_id,password_id    密码:password_id,密码

(2)是否比(1)“更规范”?

在这个OP的情况下,只要: 1)我们将地址视为原子价值, 2)应用程序只需要这三种类型的地址。

然后将每个地址存储在不同的列中同样有效。第二种解决方案不会将地址分解为其组件(国家,城镇,街道等)。因此它不比第一个更“规范化”!

答案 6 :(得分:1)

如果你有令人信服的理由,我的两分钱就是你所描述的方式正常化 OK 。有时,这个原因可以像高度自信一样简单,你永远不需要规范化的形式。正如Stefan Mai暗示的那样,如果您只需要使用您指定的三种类型的地址,那么只检索和更新单个表会更容易。另一方面,如果三个地址要求有可能发生变化那么它可能会发生变化,而且早期就是规范化的最佳时机。