关系数据库模式设计 - 如何直接从实体的字段集建模一对一映射

Posted

技术标签:

【中文标题】关系数据库模式设计 - 如何直接从实体的字段集建模一对一映射【英文标题】:Relational DB schema design - How to model 1-to-1 mapping right from an entity's set of fields 【发布时间】:2019-04-26 22:10:45 【问题描述】:

我正在为电子商务环境中的 Customer 实体开发 mysql(通过 Prisma 数据模型)架构。随着字段数量的爆炸式增长(包括参与度跟踪),我在这里有两种可能的设计:

type Customer 
  email: String! @unique
  name: String
  birthDate: DateTime
  addresses: [Address!]!
  ...
  productsVisited: [Product!]!
  productsShared: [Product!]!
  productsSearched: [Product!]!
  ...

应该将传递信息的字段或字段一起划分到自己的表中,并通过一对一的关系连接到前一个表:

 type Customer 
  profile: CustomerProfile! @relation(name: "CustomerProfile", onDelete: CASCADE)
  addresses: [Address!]!
  ...
  productEngagements: ProductEngagement! @relation(name: "CustomerProductEngagements", onDelete: CASCADE)
  ...


type CustomerProfile 
  customer: Customer! @relation(name: "CustomerProfile", onDelete: SET_NULL)
  email: String! @unique
  name: String
  birthDate: DateTime


type ProductEngagement 
  customer: Customer! @relation(name: "CustomerProductEngagements", onDelete: SET_NULL)
  productsVisited: [Product!]!
  productsShared: [Product!]!
  productsSearched: [Product!]!

问题:

    这里的正确设计思维方式是什么?我目前由我的 ER 图和直觉驱动。通过使表格变薄 w.r.t,我是否会获得或失去任何执行或灵活性优势?列数?

    在第二种方法中,查询是否需要为表连接做额外的工作?

抽象问题:

考虑到不断发展的模式或性能标准,一种方法肯定比另一种更好吗?或者,这只是口味问题?

【问题讨论】:

将两个数据库表以 1:1 的关系通常是糟糕的设计。如果您提供CREATE TABLEs,从数据库的角度来看,我可能有更具体的 cmets。 (或者我可以同意做 1:1。) 嗨@Rick,我理解你的意思,并且我得出了相同的结论,即在应用程序服务器级别而不是在数据库中更好地进行分组列的抽象。我请求您将您的设计观点作为纯粹的 RDBMS 技术答案提供,详细说明“为什么选择 X”,以便我可以学习逻辑并接受它。 (另外,我没有 CREATE TABLEs,因为我使用的是 Prisma ORM API)。非常感谢;期待您的回答! 如果您正在转向实体-属性-值模式模式,我建议您查看 EAV 标签;有充分的理由避免它。 @RickJames 我已经经历过 EAV 发布警告,但仍然选择在我的产品-变体关系中使用它(仅)。 [在这种情况下人们对它更轻:)] 用户和 MySQL 之间可能有一百多个第三方包。我无法开始跟上他们。也许我见过一次graphql,但从来没有见过棱镜。我在这个论坛上花了很多时间帮助用户学习 MySQL,因为他们选择的包未能充分地将他们与 MySQL 隔离开来。祝你好运。 【参考方案1】:

这两种方法肯定都行得通,而且确实在一定程度上取决于您的个人偏好以及您在处理数据时想要使用的 API。

使用您概述的第二种方法,Prisma 确实会为CustomerProfileProductEngagement 和所有其他类型创建表(作为一般规则,Prisma 将所有类型定义从您的数据模型映射到它们自己的表)。因此,正如@rick-james 指出的那样,在检索数据时可能需要在JOINs 中执行开销。

这里的正确设计思维方式是什么?我目前受我的 ER 图驱动。

这通常是一种有效的方法,因为 Prisma 数据模型最终会映射到数据库。从这个意义上说,用 ER 图来考虑应该没问题。

另外请注意,Prisma 很快将支持嵌入类型,它允许在 Prisma 数据模型中定义一个类型,该类型不获取自己的表,而是将数据存储在内部的 JSON 列中非嵌入式类型的表。目前在将 Prisma 与 MongoDB 一起使用时已支持此功能,但尚未与 SQL 一起使用。您可以在this feature request 中了解有关此主题的更多信息。

【讨论】:

谢谢@nburk,我一直在等待您的意见,这确实是一个详细的答案。我想我会在 Prisma 级别将这些字段保留在单一类型中,并且会像在应用程序 graphql 服务器(Apollo v2)级别的第二种情况一样破坏它们。你认可这个设计吗?【参考方案2】:

在 RDBMS 中,很少有充分的理由以 1:1 的关系将一个表拆分为两个表。将JOIN 重新组合到SELECT 中当然是可能的,但会增加一些开销、代码复杂性,并且会对性能造成轻微影响。

话虽如此,我(很少)遇到这种“垂直分区”有益的情况。

一个表中有“太多”列,拆分可以避免达到某些数据库限制...(这种情况可能会通过其他解决方案得到更好的处理。) 有些列既庞大又很少使用...通过将它们移动到单独的表中,通常可以避免JOIN,并且通常可以避免因“庞大”而对性能造成的潜在影响。 您需要将一些列添加到一个巨大的表中,但是ALTER TABLE 命令的成本太高(想想,停机时间),以至于您迫切希望找到一种方法来避免它......这样一个单独的表又快又容易等(当然,需要JOIN会带来不便等。) 您有一组很少出现在表中的列...在将它们拆分之前,您打算用NULLs 填充这些列(这是一个有效的做法)。但是在将它们分开之后,您在另一个表中根本没有一行。然后使用LEFT JOIN,从而凭空重构NULLs

【讨论】:

诚然,由于我处于起步阶段,我还没有遇到过这些极端情况,但我认为第二种情况确实与我的直觉产生了共鸣。这是一个要保留的清单!谢谢瑞克,我希望我能在这个列表上提出问题:) 多年前,我通过 MyISAM 看到了对第二个项目的需求。 InnoDB 在“为你做这件事”方面做得很好。如果一条记录超过 8KB,它会自动且静默地将一些列值移动到另一个块中。这通常是双赢的,你不需要了解细节,也不需要试图“比它更聪明”。

以上是关于关系数据库模式设计 - 如何直接从实体的字段集建模一对一映射的主要内容,如果未能解决你的问题,请参考以下文章

从E-R模型到关系模型

数据库中的实体,实体型,实体集解析

数据库系统--如何将ER转化为关系模式

数据库系统--如何将ER转化为关系模式

数据库系统--如何将ER转化为关系模式

如何在 Xcode 4 中建模这种关系?