postgresql 中低复杂度数据库中外键的使用

3hvapo4f  于 2023-06-29  发布在  PostgreSQL
关注(0)|答案(1)|浏览(147)

我正在尝试重新设计一个低-中等复杂度的数据库模式。我正在决定要包含多少个外键。

通常是使用大量的外键将实体/表紧密地连接在一起更好,还是使用尽可能少的外键将实体/表连接在一起更好?你的底线在哪里?

正如https://stackoverflow.com/a/1876062/12713117中所指出的,“外键与连接的关系比与保持数据库完整性的关系要小。证明这一点的是,您可以以任何您想要的方式连接表,即使是以不一定有意义的方式。
在我的例子中,在我的数据库模式中的一些表之间有很多关系,但不是所有表。该图类似于一个密集的网络,而不是一个有序的轴辐或类似的可识别的图。
Entity Framework设置的许多外键列都是可选的,因为数据是逐段导入的,严格的要求不允许我执行导入过程的每一段。在导入过程的所有部分完成后,数据最终会变得一致。这给了我一个显而易见的解决方案:我在不中断导入过程的情况下尽可能地设置外键。
不幸的是,这会导致一些表高度互连,并产生代码气味。

代码气味问题

在构建系统时,完成我的主要用例“获取创建分析所需的所有相关数据”所需的表连接数量增加了。我现在有一个大的PostgreSQL视图,其中包含许多表,并且随着我对系统的改进,它将变得更大。我不喜欢维护这个非常大的查询。某些数据片段只需要一个连接,但其他数据片段则需要连接上连接上连接。这看起来像是一个只有几张表的系统的代码味道。

系统

这是一个与药房福利相关的系统。在将基于标准的数据收集起来并与内部数据混合后,才在大型SQL查询中呈现给员工。工作人员分析该数据并创建建议。
从CRUD的Angular 来看,这个系统很简单。从性能的Angular 来看,我认为没有理由不能拥有尽可能多的外键。
从查询的Angular 来看,唯一困难的查询是收集所有信息以创建分析。如前所述,这需要几个表连接。

表格

在“评论更新”中的问题末尾讨论。

潜在解决方案

输入驱动方式

按源对数据进行分段,并编写服务和查询以分别从这些源提取数据。
问题是,尽管数据来自不同的源,但数据本身在不同的源之间是相互关联的,并且来自这些源的数据之间存在直接的外键关系。

领域驱动方式

这并不能解决代码的味道。所有数据都在同一个域中。

星型

我试图划分数据,我把一个主要包含外键的表放在中间(一个集线器),引用其他主要包含外键的表(一个集线器),并将这些表连接到集线器,集线器彼此连接。这样做是为了减少每个表中外键的数量。
这并不起作用,因为不同中心中的实体之间的关系比同一中心中的实体之间的关系更强。Claim-Prescription-Pharmacy-Prescriber-Plan-PlanMember-Formulary-FormularyRow-Analysis-Suggestion都是内在相关的,因此我需要一个中心,并将所有这些实体作为该中心的辐条。太蠢了

混乱图

我有一个实体关系的混乱图。我认为这会在查询中产生代码气味,但也许我应该接受它?

评论更新

一位评论者要求提供有关实体之间关系的信息。
| 关系|其他实体| Other Entity |
| - -----|- -----| ------------ |
| 数量:1|处方| Prescription |
| 数量:1|制药| Pharmacy |
| 数量:1|开处方者| Prescriber |
| 数量:1|计划| Plan |
| 数量:1|计划成员| Plan Member |
| 数量:1|处方集| Formulary |
| 数量:1|处方集行| Formulary Row |
| 许多:许多|分析| Analysis |
| 许多:许多|建议| Suggestion |
| 数量:1|开处方者| Prescriber |
| 数量:1|计划成员| Plan Member |
| 数量:1|处方集行| Formulary Row |
| 许多:许多|分析| Analysis |
| 许多:许多|建议| Suggestion |
| 许多:许多|计划| Plan |
| 许多:许多|分析| Analysis |
| 许多:许多|建议| Suggestion |
| 许多:许多|分析| Analysis |
| 1:很多|建议| Suggestion |
| 1:很多|计划成员| Plan Member |
| 一比一|处方集| Formulary |
| 1:很多|分析| Analysis |
| 1:很多|建议| Suggestion |
| 数量:1|处方集| Formulary |
| 1:很多|分析| Analysis |
| 1:很多|建议| Suggestion |
| 1:很多|处方集行| Formulary Row |
| 1:很多|分析| Analysis |
| 1:很多|建议| Suggestion |
| 许多:许多|分析| Analysis |
| 许多:许多|建议| Suggestion |

| 1:很多|建议| Suggestion |

a5g8bdjr

a5g8bdjr1#

“Joins on joins on joins”不是代码味道,而是规范化。虽然将模式去规范化为具有可从关系中受益的跨表复制的FK的“web”可以简化您阅读的查询,但它在引入可能变得不一致的多个真实来源时引入了严重得多的问题。
以“索赔”、“处方”和“开处方者”之间的关系为例。
选项1:标准化处方具有ClaimId和PrescriberId。如果我正在加载一个索赔,我想要处方,我会通过处方。(索赔。处方。开处方者)
选项2:非规范化处方具有ClaimId和PrescriberId,并且处方者具有ClaimId。如果我正在加载一个Claim,并且我想要处方者,我可以通过快捷方式直接加入处方者。
现在的问题是,我有两个索赔和处方之间的路线。我可以去开处方还是开处方。对于加入此处方器的处方,如何确保Prescriber.ClaimId始终与Prescription.ClaimId匹配?
老实说,我认为你在过度思考这个相对简单的系统的设计方面走得太远了。数据库引擎被设计为非常快速和高效地索引和解析数据。这不是你需要担心优化的东西,除非你正在构建一个足够大的系统,需要担心它。
就编写查询而言,我强烈建议学习导航属性,然后利用EF将Linq表达式转换为适合您想要显示或使用Select工作的扁平模型。EF/Linq不是C#编写SQL的替代品,因为您不需要手动将实体“连接”在一起。导航属性允许EF完全在后台计算连接。Join是为那些边缘情况提供的,在这些情况下,实体之间存在不能用FK表示的关系。(即OwnerType + OwnerId,用于可以通过单个ID列引用2个或更多其他表的反模式表)

相关问题