12

假设我有两个关系在同一模型中保存记录,例如:

@companies1 = Company.where(...)
@companies2 = Company.where(...)

我怎样才能找到这两种关系的交集,即只有那些同时存在于两者中的公司?

4

5 回答 5

12

默认情况下,将它们连接where在一起会创建 AND,这就是您想要的。

这么多是:

class Company < ActiveRecord::Base
  def self.where_1
    where(...)
  end
  def self.where_2
    where(...)
  end
end

@companies = Company.where_1.where_2

====== 已更新 ======

有两种情况:

# case 1: the fields selecting are different
Company.where(:id => [1, 2, 3, 4]) & Company.where(:other_field => true)
# a-rel supports &, |, +, -, but please notice case 2

# case 2
Company.where(:id => [1, 2, 3]) & Company.where(:id => [1, 2, 4, 5])

# the result would be the same as
Company.where(:id => [1, 2, 4, 5])
# because it is &-ing the :id key, instead of the content inside :id key

因此,如果您是第 2 种情况,您将需要像 @apneadiving 评论的那样做。

Company.where(...).all & Company.where(...).all

当然,这样做会发出两个查询,并且很可能查询到比您需要的更多的结果。

于 2011-06-22T05:52:23.967 回答
9

我用这种方式解决了类似的问题

Company.connection.unprepared_statement do
  Company.find_by_sql "#{@companies1.to_sql} INTERSECT #{@companies2.to_sql}"
end

我们在这里需要unprepared_statement块,因为最新的 Rails 版本使用准备好的语句来加速 arel 查询,但我们需要纯 SQL。

于 2013-08-28T13:43:12.483 回答
7

使用 sql 关键字 INTERSECT。

params1 = [1,2,4]
params2 = [1,3,4]
query = "
SELECT companies.* FROM companies
WHERE id in (?,?,?)
INTERSECT
SELECT companies.* FROM companies
WHERE id in (?,?,?)
"
Company.find_by_sql([query, *params1, *params2])

它会比以前的解决方案更快。

于 2012-07-02T19:08:25.813 回答
1

你可以使用ActiveRecord::SpawnMethods#merge

例子:

Company.where(condition: 'value').merge(Company.where(other_condition: 'value'))
于 2015-10-07T11:03:06.577 回答
0

对于任何被 Rails4 卡住并且不能使用 Rails5 .or 语法的人:

我有大量动态的大查询,它们有相似的条件(因此也有相似的结果)。我的 rake 服务器会出现问题,因为它们会同时被实例化、转换为数组然后合并。

我需要一个 ActiveRecord::Relation(尚未触发)与 find_each 一起使用。

看起来像这样:

Class Conditions
  def initialize
    self.where_arr = []
    self.joins_arr = []
  end

  def my_condition1
    where_arr << 'customers.status = "active"'
    joins_arr << :customer
  end

  def my_condition2
    where_arr << 'companies.id = 1'
  end
end

conditions = []    
joins = []
# probably call them in a .each block with .send
conditions1 = Conditions.new
conditions1.my_condition1
conditions1.my_condition2
conditions << "(#{conditions1.where_arr.join(' AND ')})"
joins << conditions1.joins_arr

conditions2 = Conditions.new
conditions2.my_condition1
joins << conditions2.joins_arr

Company.joins(joins).where(conditions.join(' OR '))

=> SELECT companies.* FROM companies 
INNER JOIN customers ON companies.customer_id = customers.id 
WHERE (customers.status = 'active' AND companies.id = 1) OR
(customers.status = 'active')

它有点老套,但它可以工作,直到你有希望迁移到 Rails 5

于 2018-04-17T12:36:06.290 回答