提问者:小点点

在数据库列中存储分隔列表真的那么糟糕吗?


想象一个带有一组复选框的web表单(可以选择其中的任何一个或所有复选框)。 我选择将它们保存在以逗号分隔的值列表中,这些值存储在数据库表的一列中。

现在,我知道正确的解决方案是创建第二个表并适当地规范化数据库。 实现easy解决方案的速度更快,我希望能够快速获得该应用程序的概念验证,而不必花费太多时间。

我认为节省的时间和更简单的代码在我的情况下是值得的,这是一个可辩护的设计选择,还是我应该从一开始就规范化它?

这是一个小型的内部应用程序,它基本上取代了存储在共享文件夹中的Excel文件。 我这么问也是因为我在考虑清理程序,让它更有可维护性。 其中有一些我不太满意的地方,其中一个就是这个问题的主题。


共3个答案

匿名用户

除了由于一组重复的值存储在单个列中而违反第一范式之外,逗号分隔的列表还有许多其他更实际的问题:

  • 无法确保每个值都是正确的数据类型:无法阻止%1,%2,%3,banana,%5
  • 无法使用外键约束将值链接到查找表; 无法强制引用完整性。
  • 无法强制唯一性:无法阻止%1,%2,%3,%3,%3,%5
  • 在不获取整个列表的情况下,无法从列表中删除值。
  • 存储的列表长度不能超过字符串列的长度。
  • 难以在列表中搜索具有给定值的所有实体; 您必须使用低效的表扫描。 可能必须求助于正则表达式,例如在MySQL中:
    idList REGEXP'[[:<::]]2[[:>:]]'*
  • 难以对列表中的元素进行计数,或执行其他聚合查询。
  • 难以将值连接到它们引用的查找表。
  • 难以按排序顺序获取列表。

为了解决这些问题,您必须编写大量的应用程序代码,重新发明RDBMS已经更有效地提供的功能。

逗号分隔的列表是非常错误的,所以我把它作为我的书《SQL反模式:避免数据库编程的陷阱》的第一章。

有时您需要使用反规范化,但正如@OMG Ponies提到的,这些都是例外情况。 任何非关系“优化”都有益于一种类型的查询,而牺牲了数据的其他用途,因此请确保您知道哪些查询需要特别处理,从而值得对它们进行反规范化。

*MySQL8.0不再支持这种词边界表达式语法。

匿名用户

“原因之一是懒惰”。

这敲响了警钟。 你应该这样做的唯一原因是你知道如何以“正确的方式”去做,但是你已经得出结论,有一个有形的理由不这样做。

话虽如此:如果您选择以这种方式存储的数据是您永远不需要查询的数据,那么可能有理由以您选择的方式存储它。

(有些用户会质疑我上一段的说法,说“你永远不可能知道未来会添加什么需求”。这些用户要么是被误导了,要么是在陈述一种宗教信仰。有时候,按照你面前的需求工作是有利的。)

匿名用户

这样问有很多问题:

  • 如何从逗号分隔的列表中获取特定值的计数
  • 如何从逗号分隔的列表中获取仅具有相同2/3/etc特定值的记录

逗号分隔列表的另一个问题是确保值一致--存储文本意味着可能出现错别字。。。

这些都是非规格化数据的症状,并且强调了为什么您应该始终为规格化数据建模。 反规范化可以是一种查询优化,在实际需要时应用。