2015-04-16 31 views
3

假設我有三個型號,設立這樣的:預加載使用包括對不相關的模型

class Student < ActiveRecord::Base 
    has_many :tests 
    has_many :cars 
end 

class Car < ActiveRecord::Base 
    belongs_to :student 
end 

class Test < ActiveRecord::Base 
    belongs_to :student 
end 

我要查詢其學生不具備汽車的所有測試。預裝。

我已經試過如下:

Test.includes(:cars) # does not work because the two are not associated
Test.joins('inner join cars ON tests.student_id = cars.student_id') # works, but it doesn't preload the Cars model in my result

我不想創建一個has_many :through關係,因爲他們真的在所有都沒有關係,但我不反對如果這是最好的解決方案。

想法?

的Rails 4.1.5
的PostgreSQL 9.3.4
紅寶石2.1.2

回答

2

一個跨三個表的連接是做到這一點的方式效率極低。 Rails甚至可能足夠聰明來實現這一點,並將其拆分爲單獨的數據庫查詢。

我會做這樣的,它有兩個簡單的查詢,而不是

student_ids_with_car = Car.select("student_id").distinct 
@tests = Test.where("student_id not in (?)", student_ids_with_car) 
+0

哦,我喜歡這個。爲什麼聯接效率低下?我認爲使用'includes'的全部目的是爲了減少查詢次數,從而提高效率? –

+1

聯接很慢。當你有非常大的表格時,它們會以指數級的速度慢下來並使用更多的內存。在很多情況下,2個(或3個或更多)快速非連接查詢可能會更有效,並且隨着表大小的增加,它們的執行時間會線性增長,而不是呈指數級增長。 –

+0

實際上,這實際上是一個很好的教訓 - 最好權衡各種選擇,而不僅僅是教科書所說的。特別是因爲教科書可能告訴你你可以做什麼,而不是你應該做什麼。 –

1

您不必在同一時間使用has_many :through協會和這些協會的協會。

Test.includes(:student => :cars) 

將包括學生和他們的汽車(默認情況下它會重新載入,您可以通過使用eager_load代替preload強制聯接基於包括)。

+0

儘管這完全回答了我在我的問題中提出的問題,我把馬克斯的標記作爲接受,因爲它回答了我不知道我應該問的問題,但是謝謝! –

相關問題