2011-06-22 32 views
9

Nói rằng tôi có hai mối quan hệ giữ các bản ghi trong cùng một mô hình, chẳng hạn như:Intersection của hai quan hệ

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

Làm thế nào tôi có thể tìm thấy các giao điểm của hai mối quan hệ này, ví dụ: chỉ những công ty đó tồn tại trong cả hai?

+0

Có vẻ bạn không muốn sql đây. Vì vậy, bạn có thể sử dụng phương thức giao cắt mảng: http://www.ruby-doc.org/core/classes/Array.html#M000274 – apneadiving

Trả lời

12

Theo mặc định, hãy kết nối những người này where với nhau tạo VÀ đó là những gì bạn muốn.

Vì vậy, nhiều là:

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

@companies = Company.where_1.where_2 

====== CẬP NHẬT ======

Có hai trường hợp:

# 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 

Vì vậy, nếu bạn đang ở trong trường hợp 2, bạn sẽ cần phải làm như những gì @apneadiving bình luận.

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

Tất nhiên, việc này sẽ gửi hai truy vấn và nhiều khả năng truy vấn nhiều kết quả hơn bạn cần.

+0

Peter, giả sử các mối quan hệ này đến qua các phương tiện khác nhau mà cả hai đều không bao gồm 'ở đâu '. Có cách nào để tính điểm giao cắt này không? – sscirrus

+0

@sscirrus Đã cập nhật câu trả lời, vui lòng xem. – PeterWong

+2

Điều này không trả về một mảng? Làm thế nào tôi có thể làm cho nó trở về tài nguyên ActiveRecord? –

5

Sử dụng từ khóa 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]) 

nó sẽ nhanh hơn giải pháp trước đó.

+0

Cảm ơn câu trả lời của bạn! Điều gì sẽ xảy ra nếu 'params1' và' params2' là cả hai mảng chiều dài không xác định? – sscirrus

+0

1. Bạn có thể thay đổi điều kiện, nếu bạn không tìm thấy bằng id [about] (http://www.w3schools.com/sql/sql_where.asp). 2. Một cái gì đó như: "... WHERE id IN (# {params1.map {'?'}. Tham gia (',')}) ..." – Michael

+0

Tôi thấy rằng '# {Array.new (params1.size , '?'). join (',')} 'là nhanh hơn. Nhưng nó không quan trọng trong trường hợp này. – Michael

3

tôi giải quyết vấn đề tương tự cách này

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

Chúng ta cần unprepared_statement khối đây vì các phiên bản mới nhất Rails sử dụng báo cáo chuẩn bị để tăng tốc độ truy vấn AREL, nhưng chúng ta cần SQL tinh khiết tại chỗ.

1

Bạn có thể sử dụng ActiveRecord::SpawnMethods#merge

Ví dụ:

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

Nó chỉ thêm một điều kiện nơi. Nó không thay thế cho INTERSECT –

Các vấn đề liên quan