为什么使用common-lookup表来限制实体的状态错误?

时间:2010-04-22 14:14:47

标签: database-design

根据Anith Sen的Five Simple Database Design Errors You Should Avoid,使用公共查找表来存储实体的可能状态是一个常见的错误。

编辑+答案: Anith的文章中的数字没有很好的标注 - 我认为图1 图2 都是示例糟糕的设计,而图2是好的设计 Phew ,在那里担心了一会儿。

总结:

  • 查找表:
  • 常见查询表:错误

我会在下面提出我的问题以供参考。


给出以下理由:

  
      
  1. “你失去了确保准确的手段   数据;限制。通过组合   不同的实体合而为一   表,你没有声明性的手段   限制一定的价值   类别。“
      如何限制值失去准确性?

  2.   
  3. “你被迫代表   每个数据类型都是一个字符串   通用查找表的类型。“
      如果我想表示其他数据类型,我可以在其查找表中添加一列。

  4.   
  5. “你自己承诺   刚性和随后的复杂性。“
      如何?

  6.   
  7. 第四,最后,你面对   与物理实现   的问题。
      我不明白为什么。

  8.   

我不同意所给出的大多数理由,并希望对我的错误进行客观批评?逻辑。

我的例子:

引用具有许多可能通常具有自然流程的状态的维修服务中的作业示例,让我们采用JobStatus表:

  1. 预订
  2. 指派给技师
  3. 诊断问题
  4. 等待客户确认
  5. 修复&准备接送
  6. 修复&快递
  7. 无法修复的&准备接送
  8. 引用被拒绝
  9. 可以说,其中一些状态可以规范化为Couriered ItemsCompleted JobsQuotes(具有待处理/已接受/拒绝状态)等表格,但这感觉就像不必要的架构复杂化。

    另一个常见的例子是OrderStatus表来限制订单的状态:

    1. 已完成
    2. 发货
    3. 取消
    4. 退款
    5. 状态标题和说明在一个地方进行编辑,并且很容易作为带有外键的下拉列表用于动态数据应用程序。这对我来说过去很有用。如果业务规则规定了新订单状态的创建,我可以将其添加到OrderStatus表,而无需重建我的代码。

      为什么这是一种不好的做法?


      编辑:我在我的问题中添加了Anith的理由,并试图保持客观。

      -

5 个答案:

答案 0 :(得分:12)

Anith Sen建议不要为所有查找代码设置一个查找表。这是他的例子中category列的重要性。每个类别都有一个单独的表是绝对可行的方法。

这是因为:

  1. 我们可以使用查找表通过强制执行外键来限制值
  2. 它使数据库更容易优化使用查找表连接数据表的查询
  3. 它更好地扩展:一个大的查找类别可以真正扭曲性能
  4. 在您的示例中,JobStatus和OrderStatus是单独的类别。适用于单独的实体。这就是为什么他们需要不同的查找表。在几个不同的数据表中共享相同的代码表甚至没有问题。当问题出现时,我们有一些单独的数据表(实体),其中某些状态是不合适的:这是将代码拆分成单独的查找表的时间。

    修改

    我看到你编辑了你的帖子,引用了所有Anith的观点。我认为最重要的一点是关于约束的第一点。如果要将ORDERS.STATUS列限制为具有OrderStatus类别的值,则必须使用单独的表来强制执行外键。您的替代方案是:

    • 在ORDERS表上包含CodeCategory列,并对公共CODES表强制执行复合外键,该表现在需要(Category,Code)的唯一键。
    • 在检查约束中复制OrderStatus值
    • 不强制执行数据库中的值,并依赖应用程序的下拉列表来限制值。

    从数据库的角度来看,所有这些选项都很糟糕。

答案 1 :(得分:8)

你已经有了正确的答案,所以这句话是额外的。

OTLT(一个真正的查找表)的一个大问题是,您最终将来自不同域的值放在同一列中,然后使用单独的列来消除歧义。

在您的示例中,您使用了每个状态描述旁边的数字。如果这些数字是数字代码,我认为它们必须是,那么你不希望值4,意思是“等待客户确认”在与值4相同的列中,意思是“已取消”。如果您这样做,那么您不能将此列用作您的一个真实查找表的PK。

如果你给你的一个真正的查找表另一列,称之为“CodeType”并使用“CodeType”和“Code”作为复合PK,你通过为每个代码单独的查找表引入了比你介绍的更多的复杂性类型。

简短的回答是:不要将来自不同域的值放在同一列中。它总是比它节省的麻烦更多。

顺便说一下,可以创建一个视图,将所有单独的查找表组合成一个看似单个巨型查找表的视图。这在某些非常特殊的情况下非常有用。

答案 2 :(得分:4)

对于您需要的每个查找,您应该有一个单独的查找表,而不仅仅是一个。当你有一个时,它就成了系统的瓶颈,因为大多数查询可能需要加入表或查询该表。另外坦率地说,当你进入公司时,它会使系统更难维护和理解。

此外,您已经破坏了具有PK / FK关系的任何数据完整性。如果您有单独的表,FK意味着您不能输入任何不在查找表中的值。如果使用一个大表,则可以输入不适合该情况的值。我记得我工作的一个数据库,其中person_type的值对于某些记录是“是”。

答案 3 :(得分:1)

问题在于创建一个具有许多不同代码的中央表。您要么a)必须包含额外的列,以确保订单仅引用该表中的订单状态,或者b)您最终发送的作业和“无法修复且准备好接收”的订单

修改

现在你已经添加了Anith的理由和拒绝原因。同样,Anith所讨论的是为数据库中的所有查找值提供一个中心表。那么让我们看看数字2:

  

“你被迫代表每一个人   数据类型为此类型的字符串   通用查找表。“

     

如果我愿意   表示另一种数据类型,我可以添加   它的列到我的查找表。

但是对于所有查找的一个中心表,您现在正在添加一个仅为一个特定查找操作填充的列。但是,您是否要添加强制执行此操作的CHECK约束(有效地创建NULL,但如果列上的Category ='X'约束,则为NOT NULL)。这可能会变成维护噩梦。

答案 4 :(得分:1)

我认为您的订单状态示例并不符合本文中讨论的内容 - OrderStatus表似乎是作者认为您应该执行此操作的方式。

“查找表结束所有查找表”的原因有很多,其中包括无法定义关系,加入变得比应该更加困难的事实。