21

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

选项 1.Customer有外键Address

   客户(id、phys_address_id、mail_address_id)
   地址(id、街道、城市等)

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

   客户ID)
   地址(id、customer_id、address_type、街道、城市等)

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

   客户(id、phys_street、phys_city 等 mail_street、mail_city 等)

我最重要的目标之一是简化对象关系映射,所以我倾向于第一种方法。你怎么认为?

4

12 回答 12

11

出于标准化的所有常见原因,我倾向于第一种方法。这种方法还可以更轻松地对邮件详细信息执行数据清理。

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

   客户(id,phys_address_id)
   Cust_address_type(cust_id、mail_address_id、address_type、start_date、end_date)
   地址(id、街道、城市等)
于 2009-03-15T20:28:26.570 回答
7

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

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

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

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

因此,根据您是否需要搬家,(1)或(2)都可以,但我会避开(3),因为您随后会重复表中地址的定义,并且您如果您更改地址的外观,则必须添加多个列。它的性能可能略高一些,但老实说,当您在关系数据库中处理正确索引的联接时,并没有太多收获,而且在某些不需要地址的情况下它可能变慢因为客户的记录大小会更大。

于 2009-03-15T21:35:59.233 回答
6

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

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 表将遵循 table-per-hierarchy 继承方案,以根据文化区分地址;所以美国地址将有一个州和邮政编码字段,但加拿大地址将有一个省和邮政编码。

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

于 2009-12-22T18:32:06.867 回答
4

第二种选择可能是我要走的路。并且如果它允许用户添加额外的地址'(如果你想让他们这样做),他们可以随意切换以进行运输等。

于 2009-03-15T20:20:00.273 回答
3

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

于 2009-03-15T20:23:11.403 回答
3

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

于 2009-03-15T21:36:55.550 回答
2

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

于 2009-03-15T21:04:12.600 回答
2

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

当需要额外的邮寄地址时,我将其放在单独的对象/表格中,以免客户对象过于混乱。

在我职业生涯的早期,我疯狂地规范了一个订单,该订单引用了一个引用了送货地址的客户。这使事情变得“干净”,但使用起来又慢又不雅。现在我使用一个只包含所有地址信息的订单对象。我实际上认为这更自然,因为客户可能会更改他的(默认?)地址,但 2007 年发送的货件地址应该始终保持不变——即使客户在 2008 年搬家。

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

于 2009-03-15T22:47:03.710 回答
1

我会选择第一个选项。在这些情况下,我对 YAGNI 感到非常厌倦(你不会需要它)。我数不清有多少次我查看过具有一对多表“只是以防万一”多年的模式。如果您只需要两个,只需使用第一个选项;如果将来需求发生变化,那么就改变它。

于 2009-03-15T20:25:38.277 回答
1

就像在许多情况下一样:这取决于。

如果您的客户处理多个地址,那么一对多关系将是合适的。您可以在地址上引入一个标志,表明地址是否用于发货或账单等。或者您将不同的地址类型存储在不同的表中,并在客户上具有多对一的关系。

在您只需要知道客户的一个地址的情况下,您为什么要对多对数进行建模?一对一的关系将满足您的需求。

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

于 2009-03-15T20:56:14.447 回答
1

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

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

如果地址发生变化,只需结束当前地址的日期并在Address表中添加一条新记录。和phys_address_id总是mail_address_id指向当前地址。

这样,您可以保留地址历史记录,您可以在数据库中存储多个邮寄地址(默认为),如果实际地址和mail_address_id邮寄地址相同,您只需指向同一记录。phys_address_idmail_address_id

于 2009-03-15T21:19:49.303 回答
1

好线。我花了一段时间考虑最合适的模式,并得出结论,quentin-starin 的解决方案是最好的,除了我在他的 PersonAddress 表中添加了start_dateend_date字段。我还决定添加注释激活删除

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

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

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

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

11 随便街 随便城市 Z1P C0D3

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

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

我很想听听人们的想法。

于 2017-02-27T12:41:03.947 回答