我不断遇到这样的场景:使用每行键/值模型而不是固定的列/字段模型将一组任意数据存储在表中会很有用。问题是,我想用正确的数据类型存储值,而不是将所有内容都转换为字符串。这意味着我必须选择具有多个可空列的单个表,每种数据类型一个,或者一组值表,每种数据类型一个。我也不确定是否应该使用完整的第三范式并将键分开到单独的表中,通过值表中的外键引用它们,还是使事情简单并存储会更好值表中的字符串键,并接受字符串的重复。

旧/坏:

由于需要定期修改表,因此该解决方案使添加其他值成为在流动环境中的痛苦。

我的 table
===========================
ID Key1 Key2 Key3
int int字符串日期
----------------------------
1值1值2值3
2值4值5值6

单表解决方案

该解决方案允许通过单个表进行简化。查询代码仍然需要检查是否为空,以确定该字段存储的数据类型。可能还需要一个检查约束,以确保只有一个值字段包含非空数据。

数据值
================================================== ===========
ID RecordID键IntValue StringValue DateValue
int int字符串int字符串日期
-------------------------------------------------- -----------
1 1键1值1 NULL NULL
2 1键2 NULL值2 NULL
3 1 Key3 NULL NULL Value3
4 2 Key1值4 NULL NULL
5 2键2空值5空
6 2 Key3 NULL NULL值6

多表解决方案

尽管代码需要事先了解数据类型,因为该代码需要为每种数据类型查询不同的表,所以该解决方案可以使每个表的用途更加简洁。索引可能更简单,更高效,因为需要索引的列较少。

整数值
===============================
ID RecordID key 值
int int字符串int
-------------------------------
1 1键1值1
2 2键1值4

字符串值
===============================
ID RecordID key 值
int int字符串字符串
-------------------------------
1 1键2值2
2 2键2值5

日期值
===============================
ID RecordID key 值
int int字符串日期
-------------------------------
1 1键3值3
2 2键3值6

您如何解决这个问题?哪种解决方案更好?

另外,如果键名由于某些原因更改,是应该将键列分隔到单独的表中并通过外键进行引用,还是应该将其保留在值表中并进行批量更新?

最佳答案

首先,关系数据库并非旨在存储任意数据。关系模型的基本原理是围绕获取将要存储的数据的性质的规范。

其次,您建议的是实体属性值(EAV)的变体。 EAV的问题在于数据完整性,报告,性能和维护。它们有自己的位置,但它们类似于毒品:在有限的数量和狭窄的情况下使用它们可能会有益;太多会杀了你。

针对EAV编写查询非常麻烦。因此,如果您要使用EAV,那么我看到它们成功的唯一情况是限制其使用,从而不允许任何人编写查询以过滤特定属性的查询。即,不允许任何人编写类似于Where AttributeName = 'Foo'的查询。这意味着您永远无法对报表的特定位置进行过滤,排序,计算或将特定属性放置在特定位置。 EAV数据只是一包分类数据,可以在报告中大量涌出,仅此而已。我什至已经看到人们将EAV实现为Xml Blob。

现在,如果您以这种方式使用EAV,并且由于它只是数据的一小部分,那么我将使用单表方法。单表方法的一个重要好处是可以添加检查约束,以确保IntValue,StringValue或DateValue列中只有一个值。空值不会花费您太多,并且如果这只是一堆数据,则不会对性能造成任何影响。此外,它可以使查询更简单,因为您可以使用简单的case语句返回String,Integer或DateValue。

我可以看到多表方法存在许多问题,其中最重要的一点是没有什么可以防止同一个属性具有多种类型的值(例如IntegerValues中的一行和StringValues中的一行)。此外,要获取数据,您将始终必须使用三个左连接,这将使查询的编写更加麻烦。

EAV的成本是纪律和警惕。它要求开发团队中的纪律人员永远不要在任何情况下针对特定属性编写报告或查询。开发人员将承受很大的管理压力,要求他们“仅此一次”编写可针对特定属性进行过滤的内容。一旦您走上了黑暗的道路,它将永远支配您的开发和维护。 EAV必须保留一堆数据,仅此而已。如果您无法在开发团队中维持这种纪律,那么我将不会实现EAV。为了避免以后发生维护噩梦,我将要求对任何新列进行规范。一旦用户确实希望对属性进行过滤,排序,计算或将其放置在报表上的特殊位置,则该属性必须成为第一类列。如果您能够在使用上保持纪律,则EAV可以很好地使用户存储所需的任何信息,并将需要获取数据元素规范的时间推迟到用户希望以前面提到的方式使用属性时。

关于database-design - 您将如何构造实体模型以存储具有不同数据类型的任意键/值数据?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2988142/

10-11 22:37
查看更多