2014-02-13 18 views
2

位置屬於一個或多個實體。一個實體可以有一個或多個位置。平均SQL在Rails 4中沒有SQL使用連接和其中

我嘗試獲取與當前位置具有相同實體的所有其他位置。

我有以下型號:

class Location < ActiveRecord::Base 
    has_many :location_assignments 
    has_many :entities, :through => :location_assignments 
    accepts_nested_attributes_for :location_assignments 
end 

class Entity < ActiveRecord::Base 
    has_many :location_assignments 
    has_many :locations, through: :location_assignments 
    accepts_nested_attributes_for :location_assignments 
end 

這是SQL我想

SELECT DISTINCT l.* FROM locations l, location_assignments la, entities e 
WHERE l.id = la.location_id 
AND la.entity_id = e.id 
AND e.id in (SELECT ee.id from entities ee, location_assignments laa 
WHERE ee.id = laa.entity_id 
AND laa.location_id = 1) 

但我不希望使用SQL。 這是我嘗試使用Rails

Location.joins(:entities => :locations).where(:locations => {:id => location.id}) 

它給了我好幾次的當前位置。行的數量與SQL相同(不區分只能獲取當前位置)。

有什麼想法?

回答

0

一種方法是模仿您使用子查詢的SQL,並使用標準的ActiveRecord查詢,而不必下降到AREL。讓我們先從子查詢:

Entity.joins(:location_assignments).where(:location_assignments => {:location_id => location.id}) 

這將返回包含所有這些都與他們相關的location.id代表的位置,就像你的SQL子查詢中的實體的關係。

然後主查詢,與子查詢爲XXXXX現在的可讀性:

Location.joins(:entities).where(:entities => {:id => xxxxx}) 

這是你的主要查詢的等價物。插入返回基本上是實體數組的子查詢(在這種情況下,關係,但效果相同)的子查詢會提示ActiveRecord將WHERE條件轉換爲IN,而不僅僅是=。 ActiveRecord也足夠聰明,可以使用每個實體的ID。所以,插上子查詢:

Location.joins(:entities).where(:entities => {:id => Entity.joins(:location_assignments).where(:location_assignments => {:location_id => location.id})}) 

注意,喜歡你的查詢,這將返回您開始使用原來的位置,以及所有共享同一實體的其他人。

我認爲這應該等於你的SQL,但看看它是如何對你的數據!

如果您希望使用自聯接使查詢更高效(這意味着您可以不使用SQL片段字符串,而是使用另一個聯接而不是2個聯接和一個帶有另一個聯接的子查詢),但是我認爲您可能需要下降到AREL是這樣的:

l = Arel::Table.new(:locations) 
la = Arel::Table.new(:location_assignments) 
starting_location_la = la.alias 
l_joined_la = l.join(la).on(l[:id].eq(la[:location_id])) 
filtered_and_projected = l_joined_la.join(starting_location_la).on(starting_location_la[:entity_id].eq(la[:entity_id])).where(starting_location_la[:location_id].eq(location.id)).project(location[Arel.star]) 
Location.find_by_sql(filtered_and_projected) 

這只是加入的所有位置,以他們的位置分配,然後使用實體ID和位置分配重​​新加入,但只有那些屬於你的起始位置,對象,它就像一個過濾器。這給了我與使用標準ActiveRecord查詢的以前方法相同的結果,但是再次看看它是如何發生在您的數據上的!