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

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

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

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


当前回答

是的,我想说真的很糟糕。这是一个合理的选择,但这并不意味着它是正确的或好的。

它打破了第一正规形式。

第二个批评是,将原始输入结果直接放入数据库,而根本不需要任何验证或绑定,这会使您容易受到SQL注入攻击。

你所谓的懒惰和缺乏SQL知识是新手的东西。我建议你花点时间好好做这件事,并把它看作一个学习的机会。

或者让它保持现状,吸取SQL注入攻击的痛苦教训。

其他回答

“其中一个原因是懒惰。”

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

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

(有些用户会对我上一段中的说法提出异议,说“你永远不知道将来会增加什么需求”。这些用户要么是被误导了,要么是在陈述宗教信仰。有时候按照摆在你面前的要求工作是有利的。)

我可能会采取中间立场:将CSV中的每个字段放到数据库中单独的列中,但不太担心规范化(至少目前如此)。在某些情况下,规范化可能会变得很有趣,但如果将所有数据都塞到一个列中,那么使用数据库实际上没有任何好处。在对数据进行有意义的操作之前,您需要将数据分离为逻辑字段/列/任何您想要调用它们的内容。

我需要一个多值列,它可以实现为一个xml字段

它可以转换为必要时分隔的逗号

使用Xquery在sql server中查询XML列表。

通过作为xml字段,可以解决一些问题。

使用CSV:不能确保每个值都是正确的数据类型:没有办法防止1,2,3,banana,5

使用XML:标签中的值可以强制为正确的类型


CSV:不能使用外键约束将值链接到查找表;没有办法强制引用完整性。

对于XML:仍然是一个问题


CSV:不能强制唯一性:没有办法防止1,2,3,3,3,5

对于XML:仍然是一个问题


使用CSV:不能在不获取整个列表的情况下从列表中删除值。

使用XML:可以删除单个项


使用CSV:很难搜索列表中给定值的所有实体;你必须使用低效的表格扫描。

使用XML: XML字段可以被索引


使用CSV:很难计数列表中的元素,或执行其他聚合查询

使用XML:不是特别难


使用CSV:很难将值连接到它们引用的查找表中

使用XML:不是特别难


使用CSV:很难按顺序获取列表。

使用XML:不是特别难


使用CSV:将整数存储为字符串所占用的空间大约是存储二进制整数的两倍。

使用XML:存储甚至比csv还要糟糕


CSV:加上很多逗号字符。

在XML中:使用标记代替逗号


简而言之,使用XML解决了带分隔符列表的一些问题,并且可以根据需要将其转换为带分隔符列表

好吧,我已经在SQL Server的NTEXT列中使用键/值对标签分离列表4年多了,现在它工作了。你确实失去了进行查询的灵活性,但另一方面,如果你有一个持久化/取消持久化键值对的库,那么这不是一个坏主意。

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

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提到的,这些都是例外情况。任何非关系“优化”都以牺牲数据的其他用途为代价,使某一类型的查询受益,因此一定要知道哪些查询需要特别处理,从而值得进行非规范化处理。