Rails:通过多对多关联查找实体

时间:2012-10-09 19:25:40

标签: ruby-on-rails performance many-to-many associations

我有模型A和模型B. A has_and_belongs_to_many Bs,反之亦然。

现在,我想在A中找到一个对象/实体,其中包含B内的某些对象(比如B1和B2)。我怎样才能在Rails中有效 ?我目前的解决方案是这样的:

A.all.select {|a| a.bs.sort == [B1, B2]}.first

它基本上遍历A中的所有对象,并检查它是否has_and_belongs_to正确的Bs。这是非常低效的。有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

您可以使用嵌套的子查询来执行此操作,这是一个有效的解决方案,但不一定是高效的,因此您必须运行一些基准测试。

以下涉及在AB之间的join_table上执行的三个嵌套查询。 您首先要确定Bexcluded_bs以外的所有B1的ID(称为B2)。然后,您可以确定哪些A与这些excluded_bs有关系,并将其称为excluded_as。不在A中的所有excluded_as都是我们想要的(称为included_as)。一旦included_as只查询A表。

excluded_bs = %(SELECT B_id FROM join_table WHERE B_id NOT IN (:included_bs))
excluded_as = %(SELECT A_id FROM join_table WHERE B_id IN (#{excluded_bs}))
included_as = %(SELECT A_id FROM join_table WHERE A_id NOT IN (#{excluded_as}))

A.where("id IN (included_as)", :included_bs => [B1.id, B2.id])

这应该为您提供与AB1完全关系的所有B2,但不包含任何其他关系。你可能能够稍微清理一下并提高效率,但它至少应该有效。

编辑:

糟糕!要修剪那些只有B1B2的广告,请尝试GROUP BY。将最后一个子查询更改为

included_as = %(SELECT A_id, COUNT(*) as Total FROM join_table WHERE A_id NOT IN (#{excluded_as}) GROUP BY A_id HAVING Total = :count)

的主要查询
Bs = [B1, B2]
A.where("id IN (SELECT A_id FROM (#{included_as}))", :included_bs => Bs.map(&:id), :count => Bs.count)

答案 1 :(得分:1)

您可以过滤habtm关联:

A.joins(:bs).where("bs.id" => [B1, B2]).first

A.joins(:bs).where("bs.id" => [B1, B2]).all

要确保仅返回具有两个关联的项目,请使用

A.joins(:bs).where("bs.id" => [B1, B2]).group("as.id HAVING COUNT(bs.id) = 2")