How to store a list in a column of a database table
因此,根据Mehrdad对相关问题的回答,我得知"正确的"数据库表列不存储列表。相反,您应该创建另一个表,该表有效地保存所述列表的元素,然后直接或通过联结表链接到它。但是,我想要创建的列表类型将由唯一项组成(与链接问题的水果示例不同)。此外,列表中的项目是显式排序的 - 这意味着如果我将元素存储在另一个表中,我每次访问它时都必须对它们进行排序。最后,该列表基本上是原子的,因为任何时候我希望访问列表,我将要访问整个列表而不仅仅是它的一部分 - 因此,必须发出数据库查询以聚集在一起的部分似乎很愚蠢列表。
AKX的解决方案(上面链接)是序列化列表并将其存储在二进制列中。但这似乎也不方便,因为这意味着我不得不担心序列化和反序列化。
有没有更好的解决方案?如果没有更好的解决方案,为什么呢?看来这个问题应该不时出现。
...更多信息让你知道我来自哪里。一旦我刚刚开始理解SQL和数据库,我就开始使用LINQ to SQL了,所以现在我有点被宠坏了因为我希望能够处理我的编程对象模型而不必考虑对象如何被查询或存储在数据库中。
谢谢大家!
约翰
更新:所以在我得到的第一批答案中,我看到"你可以使用CSV / XML路线......但不要!"。所以现在我正在寻找原因的解释。请给我一些很好的参考。
另外,为了让您更好地了解我的目标:在我的数据库中,我有一个Function表,它将包含(x,y)对的列表。 (该表还有其他信息对我们的讨论没有影响。)我永远不需要看到(x,y)对列表的一部分。相反,我将采取所有这些并在屏幕上绘制它们。我将允许用户拖动节点以偶尔更改值或向绘图添加更多值。
不,没有"更好"的方法将一系列项目存储在一个列中。关系数据库专门设计用于存储每行/列组合的一个值。要存储多个值,必须将列表序列化为单个存储值,然后在检索时对其进行反序列化。没有其他方法可以做你正在谈论的事情(因为你所谈论的是一个坏主意,一般来说,永远不应该这样做)。
我知道你认为创建另一个表来存储该列表是愚蠢的,但这正是关系数据库的作用。你正在进行一场艰苦的战斗并且没有任何理由违反关系数据库设计的最基本原则之一。既然你声明你刚学习SQL,我强烈建议你避免这个想法,并坚持使用经验丰富的SQL开发人员推荐给你的实践。
您违反的原则称为第一范式,这是数据库规范化的第一步。
存在过度简化事物的风险,数据库规范化是根据数据定义数据库的过程,因此您可以针对它编写合理,一致的查询并能够轻松维护它。规范化旨在限制数据中的逻辑不一致和损坏,并且有很多级别。关于数据库规范化的维基百科文章实际上非常好。
基本上,规范化的第一个规则(或形式)表明您的表必须表示关系。这意味着:
- 您必须能够将一行与任何其他行区分开来(换句话说,您的表必须具有可用作主键的内容。这也意味着不应重复任何行。
- 数据的任何排序必须由数据定义,而不是由行的物理排序定义(SQL基于集合的概念,这意味着您应该依赖的唯一排序是您在查询中明确定义的顺序)
- 每个行/列交集必须包含一个且只包含一个值
最后一点显然是这里的重点。 SQL旨在为您存储您的集合,而不是为您提供一个"桶"供您自己存储集。是的,这是可能的。不,世界不会结束。但是,您已经通过立即使用ORM来了解SQL以及与之相关的最佳实践。 LINQ to SQL非常棒,就像图形计算器一样。然而,同样地,它们不应该被用来代替它们所采用的过程实际上如何工作。
您的列表现在可能完全是"原子的",并且对于此项目可能不会更改。但是,你会养成在其他项目中做类似事情的习惯,并且你最终(很可能很快)遇到一个场景,你现在正在拟合你的快捷n列表中的列表接近完全不合适的方法。在为您要存储的内容创建正确的表时,没有太多额外的工作,并且当他们看到您的数据库设计时,您不会被其他SQL开发人员嘲笑。此外,LINQ to SQL将会看到您的关系,并自动为您的列表提供适当的面向对象的接口。为什么你会放弃ORM为你提供的便利,以便你可以执行非标准和不明智的数据库hackery?
你可以一起忘记SQL,并采用"NoSQL"方法。 RavenDB,MongoDB和CouchDB成为可能的解决方案。使用NoSQL方法,您不使用关系模型。您甚至不受约束模式。
我见过许多人这样做(这可能不是最好的方法,如果我错了,请纠正我):
我在下面给出了我在示例中使用的表格(该表格包含您给予特定女友的昵称。每个女朋友都有一个唯一的ID):
1 | nicknames(id,seq_no,names) |
假设您希望在id下存储许多昵称。这就是我们包含
现在,将这些值填充到表中:
1 | (1,1,'sweetheart'), (1,2,'pumpkin'), (2,1,'cutie'), (2,2,'cherry pie') |
如果你想找到你给你的女朋友id 1的所有名字,你可以使用:
1 | SELECT names FROM nicknames WHERE id = 1; |
除了其他人所说的,我建议你用更长的时间分析你的方法,而不仅仅是现在。目前的情况是物品是独一无二的。目前,诉诸物品需要新的清单。几乎要求该列表目前很短。即使我没有域名细节,但认为这些要求可能会发生变化并不是一件容易的事。如果您对列表进行序列化,那么您将陷入一种在更规范化的设计中不必要的不??灵活性。顺便说一句,这并不一定意味着完整的许多:许多关系。您可以只有一个子表,其中包含父项的外键和项的字符列。
如果您仍想沿着这条序列化列表的道路前进,您可以考虑将列表存储在XML中。某些数据库(如SQL Server)甚至具有XML数据类型。我建议XML的唯一原因是,几乎按照定义,这个列表需要很短。如果列表很长,那么通常序列化它是一种糟糕的方法。如果您使用CSV路线,则需要考虑包含分隔符的值,这意味着您必须使用带引号的标识符。假设列表很短,无论您使用CSV还是XML,都可能没什么区别。
简单的答案:当且仅当您确定该列表将始终用作列表时,然后将该列表一起加入到一个字符(例如' 0')的末尾,该字符将不会用于文本永远,并存储。然后当你检索它时,你可以用' 0'分割。当然还有其他方法来处理这些问题,但这些方法取决于您的特定数据库供应商。
例如,您可以将JSON存储在Postgres数据库中。如果您的列表是文本,并且您只是想要列表而没有进一步的麻烦,这是一个合理的妥协。
其他人已经提出了序列化的建议,但我并不认为序列化是一个好主意:关于数据库的一些巧妙之处在于,用不同语言编写的几个程序可以相互通信。如果Lisp程序想要加载它,那么使用Java格式序列化的程序将无法做到这一点。
如果你想要一个很好的方法来做这种事情,通常有阵列或类似的类型可用。例如,Postgres提供了一个类型的数组,并允许你存储一个文本数组,如果这是你想要的,并且使用JSON有类似的MySql和MS SQL技巧,而IBM的DB2也提供了一个数组类型(在他们的拥有有用的文档)。如果没有必要,这就不会那么常见了。
走这条路你会失去的是列表中的概念,它是一堆顺序的东西。至少在名义上,数据库将字段视为单个值。但如果这就是你想要的,那么你应该去做。这是你必须为自己做出的价值判断。
如果需要在列表中查询,请将其存储在表中。
如果您总是想要列表,则可以将其存储为列中的分隔列表。即使在这种情况下,除非您有非特定原因,否则将其存储在查找表中。
答案中只提到了一个选项。您可以对数据库设计进行去规范化。所以你需要两张桌子。一个表包含正确的列表,每行一个项目,另一个表包含一列中的整个列表(例如,以逗号分隔)。
这里是'传统'数据库设计:
1 2 3 | List(ListID, ListName) Item(ItemID,ItemName) List_Item(ListID, ItemID, SortOrder) |
这里是非规范化表:
1 | Lists(ListID, ListContent) |
这里的想法 - 您使用触发器或应用程序代码维护Lists表。每次修改List_Item内容时,列表中的相应行都会自动更新。如果您主要阅读列表,它可以很好地工作。优点 - 您可以在一个声明中阅读列表。缺点 - 更新需要更多时间和精力。
我只是将它存储为CSV,如果它是简单的值,那么它应该是您所需要的全部(XML非常详细,并且序列化到/从它可能是过度杀伤但这也是一个选项)。
对于如何使用LINQ提取CSV,这是一个很好的答案。
我认为在某些情况下,您可以在数据库中创建一个FAKE"列表"项目,例如,商品有几张图片来显示其详细信息,您可以连接用逗号分割的图片的所有ID并将其存储到DB,那么你只需要在需要时解析字符串。我现在在网站上工作,我打算用这种方式。
如果你真的想将它存储在一个列中并让它可查询,那么很多数据库现在都支持XML。如果不查询,则可以将它们存储为逗号分隔值,并在需要将它们分开时使用函数解析它们。我同意其他所有人,但如果你想使用关系数据库,规范化的一个重要部分就是分离这样的数据。我并不是说所有数据都适合关系数据库。如果您的大量数据不适合模型,您可以随时查看其他类型的数据库。