数据库建模最佳实践:处理与同一“基础”的关联

Database modeling best practice: Dealing with Associations to the same "base"

提问人:Jens Schmidt 提问时间:11/30/2019 更新时间:12/1/2019 访问量:45

问:

我尝试规范化我的数据库模型。但在这种情况下,我不知道该怎么做。给出以下模型:

  • 客户拥有多个系统(has_many:systems)
  • 群集具有许多系统(has_and_belongs_to_many:systems)

我想显示客户的所有系统。那将是:.这已经奏效了。@customer.systemobjects.each

然后,我可以将系统添加到集群中(我提到的是“HABTM”关联)。在我的客户视图中,我只想显示与集群无关的系统(也使用 )。Cluster.includes(:systems).where(systems: { id: sysid }).present?

现在我的问题是:我也想显示特定客户的所有集群(以及该集群的系统)。但是,现在,我只能通过系统与客户建立联系。对我来说,在集群对象中添加对客户的引用会更容易(即使我已经在系统中拥有此信息)。

我应该添加此引用吗?无论如何,它与规范化有关吗?从最佳实践的角度来看,对于一般的数据库和具体的 Ruby On Rails,您如何评估这种情况?当我只通过系统(我如何在 rails 中做到这一点)时,如何通过客户的每个集群来检查它的最佳方式?

Ruby-on-Rails 数据库 模型 关联

评论

0赞 rdnewman 12/1/2019
我担心您在集群和系统之间的 HABTM 关联。在我看来,你会希望一个系统belong_to一个集群,但反之亦然。如果你的目的是让客户找到他们的系统所属的集群,那么你必须使用某种关联,而不是直接的HABTM,它实际上有一个相当狭窄的应用。through
0赞 Jens Schmidt 12/1/2019
老实说,我不理解你的担忧。对我来说,集群由两个或多个系统(称为节点)组成。但是,在某些时候,可能有一个 - 我们称之为 - 节点 3 还不是集群的一部分(但可能在未来)。在大多数情况下,我工作(防火墙),更常见的是创建一个集群并向其添加节点。这就是我在这里想要实现的目标。我错过了什么吗?
1赞 rdnewman 12/2/2019
在我的解决方案中,系统不与集群相关联是可以的:在这种情况下,是零,这是您可以毫无问题地允许的。我所关心的是语义上的(甚至我想更多的是考虑到你并不总是有镜像关系)。从概念上讲,我看不出集群在语义上如何属于系统,因此 HABTM 不适用。因此,这是一种单向关系。我的解决方案仍然允许您指定集群并向其添加系统/节点(如果您以后愿意)。cluster_id

答:

2赞 rdnewman 12/1/2019 #1

我想你更喜欢这样的东西:

class Customer
  has_many :systems
  has_many :clusters, through :systems  # expect `clusters_id` in System, which is typical
  # ...other code
end

class System
  belongs_to :customer
  belongs_to :cluster
  # ...other code
end

class Cluster
  has_many :systems
  has_many :customers, through :systems  # expect `customer_id` in System, which is typical
  # ...other code
end

正如您的模型已经暗示的那样,这会产生三个表,但使用系统表作为其他两个表的“铰链”查找表,而不暗示集群属于系统(据我所知,这没有意义 IRL)。

我经常发现,除非你们真的有相互归属感,否则它通常比 HABTM 更容易、更好的选择。您可以转到 Rails Guides here 和 here (指南: “Active Record Associations”) 以获取有关使用的更多信息。对于您遇到的问题,绝对值得了解该指南(尽管公平地说,可能需要一些经验才能充分了解各种选项以及它们的帮助)。has_many :through:through

现在,当您想要引用客户在其中拥有系统的集群时,您只需要编写如下内容:

  my_customer = Customer.find(some_id)
  customer_clusters = customer.clusters
  customer_systems = customer.systems

若要查找群集(以及群集系统)的所有客户,请编写如下内容:through

  target_cluster = Cluster.find(some_id)
  cluster_customers = target_cluster.customers

如果你想要生成这些层次结构(例如,在它们所属的集群下分组的客户系统),它是这样的:

  my_customer = Customer.find(some_id)
  customers_systems = customer.systems.includes(:cluster)  # this brings in the associated cluster records without additional database operations

然后,根据您打算如何显示或返回数据,迭代或使用生成的数据。group_bycustomer_systems

评论

0赞 Jens Schmidt 12/1/2019
谢谢。做到了。你确定,那个系统需要有“cluster_id”吗?因为,就我而言,它可以在没有的情况下工作,并且我认为在“系统has_and_belongs_to_many集群”和“集群has_and_belongs_to_many系统”关联中,只有第三个表“clustes_systems”同时具有两个 ID 是正确的。
1赞 rdnewman 12/2/2019
通常cluster_id,如果迁移使用 .我不知道您的迁移设置,但实际上我的意思是这是系统中的外键,以知道它是什么集群。请注意,我提出的解决方案没有使用 HABTM 关联,而是在需要时使用 System 作为客户和集群之间的连接表(尽管在这种情况下,它不仅仅是一个连接表,因为 System 可能除了连接表行为之外还有自己的数据和语义)。systemst.referencecluster_idbelongs_to