我有一张带有主键ID(1,2,3)的主表。我需要存储一种关系,即书1是类似书2,书3。
我需要通过加入主表和这个关系表来找到类似的书。什么是存储它的最佳方法?
有两种方法 第一种方法:
1,2
1,3
2,3
2,1
3,1
3,2
其次是使用postgres数组:
1 -> [2,3]
2 -> [3,1]
3 -> [1,2]
有什么利弊?我们还缺少其他方法吗?
答案 0 :(得分:1)
利弊。让我们从你的第一种方法是1NF开始,而你的第二种方法不是我理解的(可能是,见下文),以及它的含义是什么。基本上你想从第一种方法开始,但请注意,在某些环境中,你必须做第二种方法。
首先我们所说的原子(提及因为大多数人都弄错了):如果一个值引用其域中的单个值,则该值是原子的。这并不排除数组,但是必须考虑将数组视为原子值(SQL数组基本上是数学矩阵,它们是序数的)。正如我在其他地方所指出的那样,如果普通性很重要,那么阵列就是原子top_five int []是原子的,就像ip_address int [](将IP地址表示为int值的有序数组)。这意味着您可以使用不能对单个元素执行的组执行操作。通常有两件事打破了原子性:集合(我认为你在这里尝试做的事情,如果我没有弄错,但是在订购sql数组时集合是无序的,所以如果你依赖于订购这不适用),以及一个数组成员在功能上依赖于另一个数组成员的情况(此处不适用)。
所以我们在这里讨论的是PostgreSQL中第一个普通形式与非第一个普通形式设计的优缺点。此外,你在这里有一个明确的例子,所以有可能不是抽象而是具体地谈论这些。
与大多数人不同,我有两种方法的实践经验。如果可以的话,我建议采用你的第一种方法,但如果你需要,可以理解第二种方法。
写并发
您的第一种方法将支持比第二种方法更好的写入并发性(并且请参阅下面有关索引的更多信息)。如果你想删除一个相似度,同时添加另一个相似度(同一本书),在你的第一种情况下,交易不会在锁定的情况下绊倒,但是在你的第二种情况下它们都会存在,因为它们都存储在同一种情况下行并要求等待行锁定。
换句话说,即使在我们开始谈论索引更新性能(这将是真正的开销)之前,第一个也会更好地扩展写入。
数据完整性
您遇到的第二个问题是数据完整性。在第一种情况下可以定义唯一的约束,这些约束无法在第二种情况下很好地表达。你可以表达它们,但它们需要维护更多的工作。如果你走这条路,你需要更仔细地思考,你可能需要编写自定义函数来检查你需要验证的内容。这是更多的工作,它也增加了写入性能(远高于唯一索引的维护。
您还必须编写约束触发器来解决删除书籍时会发生的情况。这可能是痛苦和另一个维护问题。在我参与的项目使用这种方法的情况下,我们通常会接受一些数据不一致并构建容易丢失链接的东西。这是一个非常重要的权衡,但有时是必要的。
索引和阅读效果
PostgreSQL具有GIN索引,可以轻松索引数组成员资格。某些情况下的GIN指数是您选择第二种情况的唯一原因,但它们也不是没有成本的。 GIN索引更新成本高,但读取效率高。所以人们经常在PostgreSQL中打破自动性来做存储标签数组的事情。
如果您的数据很少更新,那么您就会遇到重大的读取性能问题,并且已经耗尽了其他可能性,这种方法很有意义,特别是如果您可以在此处容忍孤立链接。
总体建议
对于一般情况,您的第一个解决方案要好得多。有些情况下第二个工作得更好,但你需要注意,当你开始这样做时会弹出很多问题....