多个where子句在同一列上具有多对多关系

时间:2014-05-04 13:06:28

标签: sql ruby-on-rails activerecord arel

设置。

我正在尝试通过RoR' ActiveRecord::Relation实现复杂的搜索查询。

到目前为止,index操作代码中的代码如下所示:

query = Member.all
query = query.search(params[:q])
query = query.includes(:structures)
query = query.with_structure(params[:party_id])
query = query.with_structure(params[:assembly_id])

@members = query.paginate(:page => params[:page])

代码背后的想法是我获得所有成员,然后在过滤器处于活动状态时删除成员。 searchwith_structures是自定义类方法,其中执行where子句。

旁注:因此,如果您的解决方案涉及使用arel,请确保向我展示如何将AREL :: NODES转换回ActiveRecord :: Relation

我的数据库如下所示:

enter image description here

会员可以改变他在一个结构中的位置,因此他/她可以有多个参与同一个结构。

问题

partyassembly过滤器都处于活动状态时,查询不返回任何内容,因为它在同一列上执行多个where子句。

我尝试了什么

Member.joins(:structures)
.where(structures: { id: [1303, 1181] })
.group("members.id").count

或者您更喜欢SQL:

SELECT COUNT(*) AS count_all, members.id AS members_id
FROM "members"
INNER JOIN "participations" ON "participations"."member_id" = "members"."id"
INNER JOIN "structures" ON "structures"."id" = "participations"."structure_id"
WHERE "structures"."id" IN (1303, 1181)
GROUP BY members.id
HAVING count(*)>1

这不起作用,因为许多记录都有一个> 1,但只是两个结构中的一个的一部分,使用OUTER LEFT JOIN也没有帮助。

我也尝试使用arel,但我无法转换回ActiveRecord。我尝试使用q1.intersect q2

我想要什么

我希望能够同时应用where语句,而不会相互抵消。我希望能够写出这样的东西:

query = Member.all
q1 = query.with_structure(params[:party_id])
q2 = query.with_structure(params[:assembly_id])
query = q1.intersect q2

提前感谢您,如果不清楚,请不要随意询问排队。 :)

2 个答案:

答案 0 :(得分:2)

您必须两次加入参与,每个ID一次。

 from members m join participation p1
 on m.id = p1.member_id and p1.structure_id = 1303
 join participation p2 on m.id = p2.member_id and p2.structure_id = 1181

我不确定你是否需要加入结构,因为你似乎没有选择它。

答案 1 :(得分:2)

我想与SO分享我作为解决问题的方法。我不会接受我自己的回答作为答案,因为@Dan Bracuk对我有所帮助而且不公平。

在控制器中:

query = Member.all
query = query.search(params[:q])

ids = params.slice(:party_id, :assembly_id).values.delete_if { |v| v.blank? }.map { |v| v.to_i }
query = query.create_joins ids

@members = query.paginate(:page => params[:page])

这是在成员模型中:

def self.create_joins structure_ids
  if structure_ids.empty?
    all
  else
    structure_ids.each_with_index.map do |id, idx|
      joins("INNER JOIN 'participations' 'p#{idx}' ON 'p#{idx}'.'member_id' = 'members'.'id'")
      .where("p#{idx}.structure_id" => id).uniq
    end.reduce(:merge)
  end
end

我们的想法是,我们为sructure_ids内的每个ID创建一个单独的连接和查询,然后使用merge方法将它们reduce放在一起。