2011-06-22 68 views
9

说我有,在同一个模型保持记录的两种关系,如:交叉的两个关系

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

我如何才能找到这两种关系的交叉点,即只存在中同时这些公司?

+0

看来你不希望这里的SQL。因此,您可以使用数组交集方法:http://www.ruby-doc.org/core/classes/Array.html#M000274 – apneadiving

回答

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 

====== UPDATED ======

有两种情况:

# 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 

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

+0

Peter,假设这些关系是通过不同的方式得到的,它们都不包含'where '。是否有手动方法来计算这个交点? – sscirrus

+0

@sscirrus更新了答案,请看看。 – PeterWong

+2

这不会返回一个数组吗?我怎样才能让它返回ActiveRecord资源? –

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]) 

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

+0

感谢您的回答!如果'params1'和'params2'都是不确定长度的数组,会发生什么? – sscirrus

+0

1.您可以更改条件,如果您发现不是通过ID [约](http://www.w3schools.com/sql/sql_where.asp)。 2.类似于:“... WHERE id IN(#{params1.map {'?'}。join(',')})...” – Michael

+0

我发现'#{Array.new(params1.size ,'?')。join(',')}'更快。但在这种情况下并不重要。 – Michael

3

我解决类似的问题,这样

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

我们需要在这里unprepared_statement块,因为最新的Rails版本使用准备好的发言加快AREL查询,但我们需要的地方纯SQL。

1

你可以使用ActiveRecord::SpawnMethods#merge

例子:

Company.where(condition: 'value').merge(Company.where(other_condition: 'value')) 
+0

它只是增加了一个where条件。它不适用于INTERSECT –