假设我有两个关系在同一个模型中保存记录,例如:
@companies1 = Company.where(...)
@companies2 = Company.where(...)
如何找到这两种关系的交集,即只有那两种关系中的公司?
答案 0 :(得分: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
当然,执行此操作会发出两个查询,并且很可能会查询比您需要的结果更多的结果。
答案 1 :(得分:5)
使用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])
它比以前的解决方案更快。
答案 2 :(得分:4)
我这样解决类似的问题
Company.connection.unprepared_statement do
Company.find_by_sql "#{@companies1.to_sql} INTERSECT #{@companies2.to_sql}"
end
我们需要unprepared_statement
阻止此处,因为最新的Rails版本使用预准备语句来加速arel查询,但我们需要纯SQL。
答案 3 :(得分:1)
您可以使用ActiveRecord::SpawnMethods#merge
示例:
Company.where(condition: 'value').merge(Company.where(other_condition: 'value'))
答案 4 :(得分:0)
对于那些坚持使用Rails4且无法使用Rails5。或语法的人:
我有一些动态数量的大查询,它们具有相似的条件(因此也有类似的结果)。当我的rake服务器一次性实例化,转换为数组然后合并时,我的rake服务器就会出现问题。
我需要一个与find_each一起使用的ActiveRecord :: Relation(尚未解雇)。
看起来像这样:
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')
它有点hacky但它确实有效,直到你可以希望迁移到Rails 5