3

这应该在数据库中表示为 1 个表还是 3 个表?我和我的朋友对此有不同的看法,所以我想看看对此的一般看法。(也许这应该是对任一解决方案的投票?)

Create Table Order
// Basic fields of the table
 - ID (Primary key)
 - CustomerID  (integer, with a FK)
 - Quantity
 - ProductID  (integer, with a FK)

 // Then depending on user selection, either these fields need to be specified 
 // (could be factored out to a separate table):
 {
 - InternalAccountID (integer, with a FK)
 - InternalCompanyID (integer, with a FK)
 }

 // Or these (could be factored out to a separate table):
 {
 - ExternalAccountNumber (free text string)
 - ExternalCompanyName (free text string)
 - ExtraInformation (free text string)
 }

1表方法:

优点:

  • 性能(一个插入而不是两个,FK 检查,无连接)
  • 可能占用更少的空间(额外的表有开销 + 索引 + 额外的 ID 字段)
  • 一张桌子而不是三张
  • 仅针对 2+3 个字段(或什么?)

缺点:

  • 可为空的字段
  • 可能额外的“类型”列(可以跳过)
  • 打破 3NF (?)

利弊请及个人意见。:)

编辑:我尝试通过使用与实际使用不同的实体来简化示例,因此任何更改模型的建议都不会真正帮助我。即请更多地关注技术方面而不是领域模型。

4

7 回答 7

3

我的意见是,如果

 // Then depending on user selection, either these fields need to be specified 
 // (could be factored out to a separate table):
 {
 - InternalAccountID (integer, with a FK)
 - InternalCompanyID (integer, with a FK)
 }

 // Or these (could be factored out to a separate table):
 {
 - ExternalAccountNumber (free text string)
 - ExternalCompanyName (free text string)
 - ExtraInformation (free text string)
 }

始终是 1:1 的订单(即,您不能有 3 个 accountID),然后将其保留为一张桌子。为了解决您的空问题,您可以再添加一个名为 InternalCustomer (boolean) 或 CustomerType (varChar) 的列,您可以使用它来定义内部或外部客户,以了解您应该查看两组字段中的哪一组特定客户。

由于我们不知道这些数据的全部用途或整个数据库的架构,因此对此的任何响应都不能真正完全限定。

于 2010-07-12T13:28:03.607 回答
3

希望这是不言自明的。

order_model_v1

于 2010-07-30T12:29:23.333 回答
0

如果您想避免数据重复,您应该使用 2 或 3 表解决方案。例如,如果您External在 Order 表中有列,则 value 可能存在多次。如果数据如下所示:

ID   ExternalCompanyName
1    ACME
2    ACME
3    My Company
4    ACME

现在,如果 ACME 将名称更改为 ACME, Inc.,您必须更新许多行。如果表已标准化,外部公司位于单独的表中,您将更新一行。请注意,可能会有一个参数将 Account Number 放在它自己的表中,但我们将把它留给极端规范化。

订单和公司/账户之间似乎不是一对一的关系,除非每个公司/账户只能有一个订单。这听起来更像是一对多的关系。

现在,如果在单表环境中更新 ExternalCompanyName 时出错,并且只有部分行得到更新,会发生什么情况。您有一些带有 ACME 的行和一些带有 ACME, Inc 的行。您最终会遇到数据错误的情况。

此外,如果这真的是一对多的关系,你真的没有节省空间。您正在按顺序复制数据,而不是在另一个表中存储一次。

于 2010-07-29T05:55:29.157 回答
0

随着数量的增加,从两张表中选择可能比一张要快得多。有时这种重构(分区)是在成熟的数据库上进行的,以提高性能。

想象一下将其用于多表连接,其中一些条件在此表上,但其他条件在不同的表中。

select from order join customer using (customer_id)
where
    order.order_date between ? and ?
    and customer.name = ?

它最终可能会order从磁盘中获取日期的所有行,然后因为它们与连接不匹配而将其中的许多行丢弃。这种从磁盘获取的速度肯定会很慢,并且可能会破坏您的 RAM 缓存。

select from order join order_detail using (order_id) join customer using (customer_id)
where
    order.order_date between ? and ?
    and customer.name = ?

在这种情况下,当它从磁盘加载所有order行时,它不会像以前那样受到伤害,因为表越来越窄。它不需要加载所有与过滤无关的冗长字段。最终,在 join to 之后customer,它只会获取order_detail符合所有条件的那些行。

如果您希望它很大,您应该考虑拆分表,以便对搜索最关键的字段在一个表中,而“数据”字段在其他一对一表中。

底线是:范式和域是一回事,但性能通常需要权衡取舍。您可以隐藏其中的一些(用视图覆盖拆分),但不是全部(为了更快的选择而复制/聚合字段)。

于 2010-07-29T06:21:06.440 回答
0

我绝对不会选择 3-table 解决方案。通过将这些数据分成 3 个表,你真的不能让任何查询在不使用外键连接的情况下返回完整的订单头,并且每次插入新订单都会更新多个表和索引,这是并发的问题。我建议使用 2 个表,一个用于 InternalOrders,一个用于 ExternalOrders。对于需要对两组订单中的数据进行合并查询的情况,请定义一个合并两个表的视图。

惊讶地看到产品 ID 和数量是订单标题的一部分。我见过的每个订单跟踪数据库都将订单项目分解为一个单独的表,使用订单 ID 作为外键,因此单个订单可以包含多个产品(或具有不同数量、交货时间等的相同产品)。 )。

于 2010-07-31T05:11:32.003 回答
0

我不是纯粹主义者,所以 3nf 在有意义的时候是好的……但你不必理所当然地认为它总是会的。

从务实的角度来看,你的目标是什么?你的优点/缺点列表是一个好的开始。我会在列表中添加更多想法——你认为合适的。

1)数据库中的任何其他表是否需要关联(例如,连接)这些数据?这就是 RDB 的意义所在。

2) 你的数据库会增长吗?即使一张桌子现在有意义,它总是有意义吗?如果您发现自己想要添加更多表,并且您的非规范化表迫使您“解决”它、处理返回的额外行、执行时间变慢等,您会后悔的。

3)当您的客户获得新的外部帐户时会发生什么,或者您有什么。你会创造一个全新的记录吗?您将如何回答诸如“客户某某的帐号是什么?”之类的问题。

...

我认为总的来说,我选择可扩展的,在这种情况下可能意味着 3nf。1 表在非常狭窄的范围内更容易处理,但如果有任何变化,您将处理“如何将此表拆分为正确相关的 3nf 表,而不会弄乱已创建的所有依赖项它?”。那个不好玩。

于 2010-08-02T22:45:51.640 回答
0

在客户订购之前,帐户信息是否与客户相关联(即,您有另一个表,您可以在其中跟踪给定 CustomerID 可以使用的帐户 ID)?您能否将所有帐户抽象为一个相当统一的模式(一个可以有几个空值),因为您有一个通用 AccountId(代理键),然后帐户的表有 3 个 varchar 字段和一个跟踪帐户类型的字段(使用用于计费等)?

如果你能做到这一点,那么你的订单只会跟踪一个 AccountID,因为订单(作为一个实体)实际上并不关心使用了哪种付款方式 - 它只关心它是该用户的合法/现有/批准的 AccountId。可以说其他一切都是别人的事(账单或检查资金等),并且该实体及其处理无论如何都需要更多数据。

这使您的订单保持清洁和无空值,并促进关注点的分离。

从概念上讲,您的订单实际上是所谓的事实表 - 仅包含数字和 FK-s,项目大小很小但数量很大。

所以:

 Table Order (
     - OrderId
     - Quantity
     - ProductId
     - DiscountId -- sonner or latter :-)
     - AccountId
     - PaymentStatus -- probaly FK as well or predefined constant
 )

 Table Account (
     - AccountId
     - BillingInfo  -- akka ext acct number as text
     - PrincialName -- akka ext company name, some equivalent for internal acct-s
     - AdditionalData
 )
于 2010-08-03T09:12:06.477 回答