首页 > 解决方案 > 两个表的一个外键会引起混乱吗?

问题描述

如果我有表 A 和表 B,并且我有从表 A 开始但在表 B 中结束的数据,并且我有一个表 C,它有一个指向 A 的主键的外键,但是当数据从 A 中删除并最终在表 B 中,它应该指向 B(与 A 的数据具有相同的 id)。这会不会造成混乱。继承人和示例来说明我的意思:

A(待定结果)id =3

B(完成的结果) null

C(user) id = 1 results id = 3(A 和 B 的外键)

三分钟后,结果出来了。

A(待定结果) null

B(完成的结果)id = 3

C(user) id = 1 results id = 3(A 和 B 的外键)

这个实现有什么问题吗。还是将 A 和 B 放在一张桌子上会更好?桌子可能会变得非常大,这是我担心的。作为单独的表,对表 A 的读取将远远大于对表 B 的读取,而表 A 会小得多,因为它只是待处理的结果。如果将 A 和 B 合并到一个表中,那么它既是待处理的,也是所有已完成结果的历史记录,因此我假设找到待处理的结果将花费更多时间。如果有什么不同的话,所有这一切都是在 postgresql 完成的。

所以我想我的问题是:这个实现是否适合中等规模,或者考虑到我刚才所说的信息,我应该将表 A 和 B 结合起来(即使 B 会无限增长,而 A 只包含当前数据并且明显更小) .

标签: postgresql

解决方案


听起来您已经发现这不起作用。我不能正确地遵循你的例子,因为“A”、“B”和“C”对我来说从来没有用过。我怀疑那些公式化的标签比其他人的具体标签更好。你赢不了 ;-) 无论如何,听起来你正面临着关于桌子大小的实际问题,并且很想使用将自然桌子分成两部分的设计。(又热又旧。)正如您所发现的,这实际上不适用于系统中的键。关系模型(等等,等等)没有“这个东西是这个或那个的孩子”的概念。所以,你在那里游泳。无论如何,这种设置非常在野外很常见,以至于它有一个名字。嗯,好几个名字。Bill Karwin 的SQL 反模式中的“多态关联”很常见。顺便说一句,这是一本好书,而且很短。同样,“混杂关联”是您会看到的一个术语。或者有时你会看到表本身被列为“跳转表”或“集线器”等。

我怀疑这种非关系模式被如此广泛使用是有原因的:它对人类有意义。关系模型总是很紧张的一个领域是当你拥有各种各样的东西时。比如,工作人员或学生。如此多的共同领域,其中一些与其特定类型不同。一张桌子?二?三?Postgres 中的表继承可能会有所帮助……至少它正在尝试这样做。无论如何,多态关系在 RDBMS 中是有问题的,因为它们不能被自动建模或约束。您需要自定义代码来确定该记录是该表的子表......或另一个表。你不能把它融入到关系中。如果您对这个设计问题的各种解决方案感兴趣,Karwin 的章节非常好,易于阅读,并且充满了替代设计。如果您不想追查这本书但有点兴趣,请查看几年前的这篇文章:

https://hashrocket.com/blog/posts/modeling-polymorphic-associations-in-a-relational-database

很有可能,您现在的兴趣更多的是日常。听起来您有一个处理管道,其中包含一些活动记录和不断增加的旧记录集合。你没有提到你的 Postgres 版本,但你可能比你想象的要担心的少。首先,您可以考虑对表进行分区。分区表有一个逻辑表,您在查询中使用一组较小的物理表与之交谈引擎盖下的桌子。您可以直接访问分区,但您不需要这样做。您只需与 my_big_table 交谈,Postgres 就会找出查找位置。因此,您可以按周、月等拆分数据,这样就没有一个桶对您来说太大了。在这种情况下,各个分区也有自己的索引。因此,您最终会得到更小的表和更小的索引。为此,您最好使用 PG 11,或者可能是 PG 10。分区是一个大主题,Postgres 功能集并不是所有情况的完美匹配……您必须在其限制范围内工作。我现在就把它留在那里,因为它可能不是你首先需要的。

比分区更简单的是一个你可能不知道的很棒的 Postgres 特性,部分索引。这不是 Postgres 独有的(SQL Server 将相同类型的功能称为“过滤”索引),但我认为 MySQL 没有它。好的,这个想法很简单:构建一个只包含匹配条件的行的索引。这是一个例子:

CREATE INDEX tasks_pending
          ON tasks (status)
       WHERE status = 'Pending'

如果您的表有 100M 记录,则完整的 B 树必须对所有 100M 行进行编目。您需要它来检查主键的唯一性……但它又大又贵。现在想象一下,您的 100M 记录只有 1,000 行,其中 status = pending。你有一个只有这 1,000 行的索引。小巧、快速、完美。这里的美妙之处在于,随着历史数据集的增长,部分索引不一定会变大。而且,对历史数据集大喊大叫,当您需要在简单的搜索中获取聚合等时,它们非常好。如果将事物拆分为多个表,则需要使用 UNION 编写更长的查询。(对于物理分区被逻辑分区主表屏蔽的分区,情况并非如此。)

高温高压


推荐阅读