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

现在,我知道正确的解决方案是创建第二个表并正确地规范化数据库。它可以更快地实现简单的解决方案,并且我希望快速地对该应用程序进行概念验证,而不必在其上花费太多时间。

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

更详细地说,这是一个小型的内部应用程序,本质上是替换存储在共享文件夹中的Excel文件。我问这个问题也是因为我正在考虑清理程序并使其更易于维护。书中有些东西我不是很满意,其中之一就是这个问题的主题。


当前回答

关于SO提问有很多问题:

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

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

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

其他回答

一般来说,只要能满足项目的要求,任何东西都是可以防御的。这并不意味着人们会同意或想要捍卫你的决定……

一般来说,以这种方式存储数据是次优的(例如,难以进行有效的查询),如果修改表单中的项,可能会导致维护问题。也许您可以找到一个中间立场,使用一个整数来表示一组位标志?

是的,有那么糟糕。我的观点是,如果你不喜欢使用关系数据库,那么寻找一个更适合你的替代方案,有很多有趣的“NOSQL”项目,它们具有一些非常高级的功能。

如果你有一个固定数量的布尔字段,你可以使用INT(1) NOT NULL(或BIT NOT NULL,如果它存在)或CHAR(0)(可空)。您也可以使用SET(我忘记了确切的语法)。

除了违反第一范式(因为在单列中存储了重复的一组值),逗号分隔的列表还有许多其他更实际的问题:

Can’t ensure that each value is the right data type: no way to prevent 1,2,3,banana,5 Can’t use foreign key constraints to link values to a lookup table; no way to enforce referential integrity. Can’t enforce uniqueness: no way to prevent 1,2,3,3,3,5 Can’t delete a value from the list without fetching the whole list. Can't store a list longer than what fits in the string column. Hard to search for all entities with a given value in the list; you have to use an inefficient table-scan. May have to resort to regular expressions, for example in MySQL: idlist REGEXP '[[:<:]]2[[:>:]]' or in MySQL 8.0: idlist REGEXP '\\b2\\b' Hard to count elements in the list, or do other aggregate queries. Hard to join the values to the lookup table they reference. Hard to fetch the list in sorted order. Hard to choose a separator that is guaranteed not to appear in the values

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

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

有时需要使用非规范化,但正如@OMG Ponies提到的,这些都是例外情况。任何非关系“优化”都以牺牲数据的其他用途为代价,使某一类型的查询受益,因此一定要知道哪些查询需要特别处理,从而值得进行非规范化处理。

关于SO提问有很多问题:

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

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

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