建模客户的最佳方式< - >地址

时间:2009-03-15 20:14:44

标签: sql database-design orm data-modeling

每个Customer都有一个实际地址和一个可选的邮寄地址。您对此进行建模的首选方式是什么?

选项1. Customer具有Address

的外键
   Customer   (id, phys_address_id, mail_address_id)
   Address    (id, street, city, etc.)

选项2. CustomerAddress具有一对多的关系,其中包含一个字段 描述地址类型

   Customer   (id)
   Address    (id, customer_id, address_type, street, city, etc.)

选项3.地址信息被去规范化并存储在Customer

   Customer   (id, phys_street, phys_city, etc. mail_street, mail_city, etc.)

我最重要的目标之一是简化对象关系映射,因此我倾向于采用第一种方法。你有什么想法?

12 个答案:

答案 0 :(得分:10)

我倾向于采用第一种方法来解决所有正常化的常见原因。这种方法还可以更轻松地对邮件详细信息执行数据清理。

如果您可能允许多个地址(邮件,住宅等)或希望能够使用有效日期,请考虑这种方法

   Customer   (id, phys_address_id)
   Cust_address_type (cust_id, mail_address_id, address_type, start_date, end_date)
   Address    (id, street, city, etc.)

答案 1 :(得分:7)

您可能需要考虑的一个重要事实(取决于您的问题域)是人们更改地址,并且可能希望在地址更改之前通知您;这对于公用事业公司,电信公司等来说确实如此。

在这种情况下,您需要有一种方法为客户存储有效日期的多个地址,以便可以提前设置地址并自动切换到正确的位置。如果这是一个要求,那么(2)的变化是建模它的唯一合理方法,例如

Customer (id, ...)
Address (id, customer_id, address_type, valid_from, valid_to)

另一方面,如果您不需要满足此要求(并且您确定将来不会),则可能(1)管理起来更简单,因为维护数据完整性要容易得多确保只存在一个相同类型的地址是没有问题的,并且连接变得更简单,因为它们只在一个字段上。

所以(1)或(2)都可以,这取决于你是否需要进行房屋搬迁,但是我会避开(3),因为你正在重复地址在表格中的定义,如果更改地址的样子,则必须添加多个列。它可能稍微高效一点,但说实话,当你在关系数据库中处理正确索引的连接时,没有太多的东西可以获得,并且在某些情况下它可能会慢一些你没有需要地址,因为客户的记录大小会更大。

答案 2 :(得分:5)

我们正在推进这样的模型:

Person (id, given_name, family_name, title, suffix, birth_date)
Address (id, culture_id, line1, line2, city, state, zipCode, province, postalCode)
AddressType (id, descriptiveName)
PersonAddress (person_id, address_id, addressType_id, activeDates)

大多数人可能认为这种情况过分。然而,我们开发的应用程序中不可否认的共同主题是,它们将拥有一些基本实体 - 人员,组织,地址,电话号码等等 - 并且他们都希望以不同的方式将它们组合在一起。因此,我们正在预先建立一些概括,我们100%确定我们有用例。

Address表将遵循每层次表的继承方案,以根据文化区分地址;所以美国地址将有州和邮政编码,但加拿大地址将有省和邮政编码。

我们使用单独的连接表来“给”一个人一个地址。这使我们的其他实体 - 人和地址 - 当我们的经验出现时,与其他实体没有联系,这往往会使事情变得复杂。它还使地址实体与许多其他类型的实体(人员,组织等)以及与链接相关的不同上下文信息(如我的示例中的activeDates)连接起来要简单得多。

答案 3 :(得分:3)

第二种选择可能就是我要去的方式。如果有机会它会让用户添加额外的地址'(如果你想让他们这样做),他们可以随意切换运输等等。

答案 4 :(得分:3)

我更喜欢#1。良好的规范化并清楚地传达意图。此模型还允许将相同的地址对象(行)用于这两个地址,我发现这些地址非常有价值。很容易因为过多地复制这些信息而迷失方向。

答案 5 :(得分:3)

在回答这些问题时,我喜欢使用DDD的分类。如果它是一个实体,它应该有一个单独的ID,如果它是一个值对象,它不应该。

答案 6 :(得分:2)

选项3限制性太强,无法在不更改架构的情况下扩展选项1以允许其他地址类型。 选项2显然是最灵活的,因此是最佳选择。

答案 7 :(得分:2)

在大多数代码中,我现在写的每个客户都有一个且只有一个物理位置。这是我们业务合作伙伴的法律实体。因此我把街道,城市等放在客户对象/表中。通常,这是可行的最简单的方法,并且有效。

当需要一个额外的邮件地址时,我将它放在一个单独的对象/表中,以免使客户对象混乱不堪。

在我的职业生涯早期,我通过订单引用一个引用送货地址的客户来规范化。这使得东西“干净”,但使用缓慢且不优雅。现在我使用一个只包含所有地址信息的订单对象。我实际上认为这更自然,因为客户可能会更改他的(默认?)地址,但2007年发送的货件地址应始终保持不变 - 即使客户在2008年搬迁。

我们目前在项目中实施VerySimpleAddressProtocol以标准化使用的字段。

答案 8 :(得分:1)

我会选择第一个选项。在这些情况下,我对YAGNI非常厌倦(你不需要它)。我无法计算我曾经看过多年来一直有多少桌子的模式的次数。如果你只需要两个,只需使用第一个选项;如果要求将来发生变化,那么就改变它。

答案 9 :(得分:1)

与许多情况一样:取决于。

如果您的客户处理多个地址,则适合多人关系。您可以在地址上引入一个标记,表示地址是用于装运还是账单等。或者您将不同的地址类型存储在不同的表中,并在客户上建立多个一对一的关系。

如果您只需要了解客户的一个地址,为什么要对那个人进行建模呢?一对一的关系可满足您的需求。

重要说明:仅在遇到性能问题时才会进行非规范化。

答案 10 :(得分:1)

我会选择选项1.如果您愿意,您甚至可以稍微修改它以保留地址历史记录:

Customer   (id, phys_address_id, mail_address_id)
Address    (id, customer_id, start_dt, end_dt, street, city, etc.)

如果地址发生变化,只需结束当前地址的日期,并在Address表格中添加新记录。 phys_address_idmail_address_id始终指向当前地址。

通过这种方式,您可以保留地址历史记录,您可以将多个邮寄地址存储在数据库中(默认情况下为mail_address_id),如果物理地址和邮寄地址相同,您只需指向phys_address_idmail_address_id在同一记录中。

答案 11 :(得分:0)

好线程。我花了一段时间考虑最合适的架构,我得出结论,除了我已经将 start_date end_date 字段添加到他的PersonAddress之外,quentin-starin的解决方案是最好的表。我还决定添加备注有效已删除

已删除用于软删除功能,因为我认为我不想仅仅通过从联结表中删除记录就会丢失先前地址的痕迹。我认为这是非常明智的,其他人可能想要考虑。如果不这样做,可以留下修改纸质或电子文档以试图追踪地址信息(最好避免的)。

备注我认为这是一项要求,但可能只是偏好。我花了一些时间在回填练习中验证数据库中的地址,一些地址可能非常模糊(例如农村地址),我认为至少允许在记录地址中保存关于该地址的注释非常有用。

我想听到意见的一件事是地址表的唯一索引(同样,请参考quentin-starin示例中的同名表。你认为它应该是应该强制执行唯一索引(作为一个复合索引,可能跨越所有非null /必填字段)?这似乎是合理的,但是由于邮政/邮政编码对于单个属性并不总是唯一的,因此可能仍然难以阻止重复数据。即使国家,省和城市字段是从参考数据(它们在我的模型中)填充的,地址行中的拼写差异可能也不匹配。唯一可以最好地避免这种情况的方法可能是运行一个或多个来自传入表单字段的数据库查询,以查看是否找到了可能的重复。另一个安全措施是让用户可以选择从已经链接到该人的数据库中的地址中选择并使用它自动填充。我认为这样可能是你只能是se的情况可以采取措施并采取预防措施来阻止重复,但只是接受它可以(并且可能会)迟早发生。

对我来说,另一个非常重要的方面是将来编辑地址表记录。假设您有两个人都列在: -

11无论什么街 无论城市 Z1P C0D3

如果将相同的地址表记录分配给不同的实体(个人,公司),是否会被视为危险?然后让我们说用户意识到其中一个人住在111 Whatever Street并且有一个错字。如果您更改该地址,它将为两个实体更改它。我想避免这种情况。我的建议是让MVC中的模型(在我的情况下,PHP Yii2)在创建一个已知与该客户相关的新地址时查找现有的地址记录(SELECT * FROM address INNER JOIN personaddress ON personaddress.address_id = address.id WHERE personaddress.person_id = {当前正在编辑的人ID})并向用户提供使用该记录的选项(如上文所述)。

我觉得将同一地址链接到多个不同的实体只是在寻找麻烦,因为这可能是拒绝以后编辑地址记录(不切实际)或冒未来编辑的风险记录可能会破坏与正在编辑地址记录的人之外的其他实体相关的数据。

我很想听听别人的想法。