2013-05-02 104 views
40

Rails> 3.2中是否可以爲由includes方法生成的連接語句添加條件?Rails包含條件

假設我有兩個模型,Person和Note。每個人都有很多筆記,每個筆記都屬於一個人。每個音符都有一個屬性important

我想找到所有人只預加重要的筆記。在SQL,這將是:

SELECT * 
FROM people 
LEFT JOIN notes ON notes.person_id = people.id AND notes.important = 't' 

在Rails中,唯一相似的方式做到這一點是使用includes(注:joins不會預裝注)這樣的:

Person.includes(:notes).where(:important, true) 

不過,這生成的SQL語句的查詢返回不同的結果集:

SELECT * 
FROM people 
LEFT JOIN notes ON notes.person_id = people.id 
WHERE notes.important = 't' 

請注意,第一個結果集包括所有的人,第二個只有人民與重要筆記相關聯。

另請注意:條件自3.1起棄用。

+0

是性能有什麼不同? notes.important ='t'在連接子句中不是語義的,因爲它只考慮一個模型。 – fotanus 2013-05-02 22:04:28

+0

是的。這是一個表現問題。我需要所有的人,如果有的話,急切地加載重要的筆記。 – gusa 2013-05-03 18:16:26

+1

我面臨同樣的問題你是如何最終管理你的查詢的?你是用純SQL編寫的嗎? – 2014-12-23 15:01:31

回答

25

根據本指南Active Record Querying

您可以在指定的條件包括預先加載這樣

Person.includes(:notes).where("notes.important", true) 

它建議使用joins反正。

針對此解決方法是創建這樣

class Person < ActiveRecord::Base 
    has_many :important_notes, :class_name => 'Note', 
      :conditions => ['important = ?', true] 
end 

相互關聯起來,然後你就可以做到這一點

Person.find(:all, include: :important_notes) 
+5

Person.includes(...)。其中(...)僅返回與重要筆記相關的人員。我需要所有人,急切地加載重要筆記。 – gusa 2013-05-03 18:18:34

+2

第二種解決方案解決了爲清晰起見而繪製的示例。但是,現實比較複雜。假設現在我需要在一段時間內(在start_date和end_date之間)重要的筆記。 – gusa 2013-05-03 18:21:45

0

一種方式是寫LEFT利用自己JOIN子句加入:

Person.joins('LEFT JOIN "notes" ON "notes"."person_id" = "people.id" AND "notes"."important" IS "t"') 

雖然不是很漂亮。

+1

但包含用於簡單加載並通過延遲加載連接作品。所以我認爲這是不合適的 – 2015-11-10 08:59:22

4

我無法使用包含Leo Correa的答案的條件。 insted的我neeed使用方法:

Lead.includes(:contacts).where("contacts.primary" =>true).first 

,或者您也可以

Lead.includes(:contacts).where("contacts.primary" =>true).find(8877) 

最後一項會取得領先,ID 8877,但只包括其主要聯繫人

8

的Rails 4.2以上版本:

選項A - 「預加載」 - 多個選擇,使用「ID IN(...)「)

class Person < ActiveRecord::Base 
    has_many :notes 
    has_many :important_notes, -> { where(important: true) }, class_name: "Note" 
end 

Person.preload(:important_notes) 

SQL:

SELECT "people".* FROM "people" 

SELECT "notes".* FROM "notes" WHERE "notes"."important" = ? AND "notes"."person_id" IN (1, 2) 

選項B - 」eager_load「 - 一個巨大的選擇,使用 」LEFT JOIN「)

class Person < ActiveRecord::Base 
    has_many :notes 
    has_many :important_notes, -> { where(important: true) }, class_name: "Note" 
end 

Person.eager_load(:important_notes) 

SQL:

SELECT "people"."id" AS t0_r0, "people"."name" AS t0_r1, "people"."created_at" AS t0_r2, "people"."updated_at" AS t0_r3, "notes"."id" AS t1_r0, "notes"."person_id" AS t1_r1, "notes"."important" AS t1_r2 
FROM "people" 
LEFT OUTER JOIN "notes" ON "notes"."person_id" = "people"."id" AND "notes"."important" = ? 
0

對於有興趣的人,我想這其中記錄屬性是假的

Lead.includes(:contacts).where("contacts.primary" => false).first 

,這是行不通的。不知怎的,爲布爾只有true的作品,所以我把它周圍的包括where.not

Lead.includes(:contacts).where.not("contacts.primary" => true).first 

這工作完全

5

Rails的5+語法:

Person.includes(:notes).where(notes: {important: true}) 

嵌套:

Person.includes(notes: [:grades]).where(notes: {important: true, grades: {important: true}) 
+0

這不是因爲它在這裏提到:[活動記錄指南](http://guides.rubyonrails.org/active_record_querying.html)。它不返回與Note無關的Person對象。它只會返回具有master屬性等於true的註釋的人員 – bkdir 2017-09-16 07:02:06