Postgres 11.7 中具有太多连接或多态关系的 table 的设计问题

Design question for a table with too many joins OR polymorphic relations in Postgres 11.7

有人给了我一个 table,但我不确定如何设计。我希望得到一些设计建议,或指向正确方向的指示。 table 称为 edge,用于存储一些事件跟踪,以及 link 到 可能 查找主机的 ID table秒。除了 ID 之外的所有内容,这里是 table 包含的所有 UUID:

ID
InvID
OrgID
FacilityID

FromAssemblyID
FromAssociatedTo
FromAssociatedToID
FromClinicID
FromFacilityDepartmentID
FromFacilityID
FromFacilityLocationID
FromScanAtFacilityID
FromScanID
FromSCaseID
FromSterilizerLoadID
FromWasherLoadID
FromWebUserID

ToAssemblyID
ToAssociatedTo
ToAssociatedToID
ToClinicID
ToFacilityDepartmentID
ToFacilityID
ToFacilityLocationID
ToNodeDTS
ToScanAtFacilityID
ToScanID
ToSCaseID
ToSterilizerLoadID
ToUserName
ToWasherLoadID
ToWebUserID

可能加入的 ID 数量非常多。我记得读过 Postgres 规划器会在你有十几个以上的连接时放弃。这个想法是,有太多的排列需要探索,规划时间可能很快就会超过查询时间。如果你把它归结起来,"from" 和 "to" link 在所有这些字段中只会有一个键值。因此,实现为 polymorphic/promiscuous 关系,如下所示:

ID
InvID
OrgID
FacilityID
FromID
FromType
ToID
ToType
ToWebUserID

这个 table 会很大,所以速度 is/will 是一个考虑因素。

我鼓励作者不要使用多态设计,尽管吸引力是显而易见的。 (我喜欢 Karwin 的 SQL Antipatterns 一书。)但是现在,面对近三打 ID,我有点不知所措。

这种问题有通用的解决方法吗?也就是说,你在哪里有一个像这样的中央 table 连接到各种可能的 table?我没有数据仓库背景,但这看起来有点像。 (此 table 的作者读过 Kimball 的书,但也没有做过任何数据仓库实现。)

重要提示:我们正在使用 JOIN 查找可能会更改的相关值,我们 而不是 使用它来更改结果集的大小.假装它永远是 LEFT JOIN.

考虑到这一点,我想到的是跳过加入 FromTo ID,而是使用自定义函数调用从相关 tables。喜欢(伪代码)

GetUserName(uuid) : citext
...and os on for other values of interest in this and other tables...

当 UUID 为 0000 等时,该函数将 return ''

我明白这不是 SO 历史上最清晰的问题,我希望能得到指向富有成果的方向的指示。

根据您“记得读过”的内容,这有点“过早优化”(这是罪恶之源)的味道,所以也许对连接优化的一些启发会有所帮助。

我在处理此类问题时遵循的一个经验法则是对事物进行建模,以便您的查询变得简单自然。经验表明,这通常会带来良好的性能。

我假设你显示的table是一个星型模式的事实table,外键指向多个维度table,这样你的查询就会看起来像

SELECT ...
FROM fact
   JOIN dim1 ON fact.dim1_id = dim1.id
   JOIN dim2 ON fact.dim3_id = dim2.id
   JOIN dim3 ON fact.dim3_id = dim3.id
   ...
WHERE dim1.col1 = ...
  AND dim2.col2 BETWEEN ... AND ...
  AND dim3.col3 < ...
  ...

现在PostgreSQL默认只考虑前8个table(join_collapse_limit)的所有join排列,其余table只按顺序加入它们出现在查询中。

此外,如果 table 的数量达到阈值 12 (geqo_threshold),遗传查询优化器 接管,一个组件通过随机选择的执行计划(真的!)通过突变和适者生存来模拟进化,因此并不总是为相同的查询提出相同的执行计划。

所以我的建议是以前七个维度 table 最有可能最显着地减少结果行数的方式编写查询(基于 WHERE 条件)。您还可以增加 join_collapse_limit,因为如果您的查询花费很长时间 运行 无论如何,您可以轻松地让计划者花更多时间思考最佳计划。

然后您将 geqo = off 设置为禁用遗传查询优化器。

如果您根据这些原则设计查询,您应该能够在不弄乱数据模型的情况下获得良好的执行计划。