【问题标题】:Database modeling best practice: Dealing with Associations to the same "base"数据库建模最佳实践:处理同一个“基础”的关联
【发布时间】:2019-11-30 10:30:57
【问题描述】:

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

  • 客户有许多系统 (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中做到这一点?),最好的方法是如何通过客户的每个集群?

【问题讨论】:

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

标签: ruby-on-rails database model associations


【解决方案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问题陈述)。

我经常发现has_many :through 通常是比 HABTM 更容易和更好的选择,除非你真的有相互归属的关系。您可以访问 Rails 指南 herehere(指南:“活动记录关联”)以获取有关使用 :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

然后根据您打算如何显示或返回数据,从customer_systems 中的结果数据中迭代或使用group_by

【讨论】:

  • 谢谢。做到了。你确定,那个系统需要有“cluster_id”吗?因为,在我的情况下,它在没有的情况下工作,我认为在“系统 has_and_belongs_to_many 集群”和“集群 has_and_belongs_to_many 系统”关联中,只有第三个表“clustes_systems”同时包含两个 id 是正确的。
  • 通常,如果您的迁移使用t.reference,则不需要在 System(表:systems)中显式指定 cluster_id。我不知道您的迁移设置,但实际上我的意思是 cluster_id 是 System 中的外键,可以知道它是哪个集群 belongs_to。请注意,我提出的解决方案不使用 HABTM 关联,​​而是在需要时使用 System 作为客户和集群之间的连接表(尽管在这种情况下,它不仅仅是一个连接表,因为可能 System 在连接之外有自己的数据和语义表行为)。
猜你喜欢
  • 2010-09-07
  • 1970-01-01
  • 2019-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-22
  • 1970-01-01
相关资源
最近更新 更多