可以在Rails中使用> 3.2为includes
方法生成的连接语句添加条件?
假设我有两个模型,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 :(得分:27)
您可以为此类
之类的预先加载指定条件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)
答案 1 :(得分:16)
Rails 5+语法:
Person.includes(:notes).where(notes: {important: true})
嵌套:
Person.includes(notes: [:grades]).where(notes: {important: true, grades: {important: true})
答案 2 :(得分:13)
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" = ?
答案 3 :(得分:4)
我无法使用包含Leo Correa答案的条件。 Insted我需要使用:
Lead.includes(:contacts).where("contacts.primary" =>true).first
或者你也可以
Lead.includes(:contacts).where("contacts.primary" =>true).find(8877)
最后一个将检索ID为8877的潜在客户,但仅包含其主要联系人
答案 4 :(得分:1)
在日语stackoverflow中讨论了相同的内容。颇为骇人听闻,但至少在第5条轨道上进行跟踪似乎可行。
Person.eager_load(:notes).joins("AND notes.important = 't'")
一个重要方面是,通过这种方式,您可以编写任意连接条件。不利的一面是您不能使用占位符,因此在将参数用作连接条件时需要小心。
答案 5 :(得分:0)
一种方法是使用连接自己编写LEFT JOIN子句:
Person.joins('LEFT JOIN "notes" ON "notes"."person_id" = "people.id" AND "notes"."important" IS "t"')
不漂亮。
答案 6 :(得分:0)
对于感兴趣的人,我在记录属性为假的情况下尝试了这个
Lead.includes(:contacts).where("contacts.primary" => false).first
这不起作用。不知何故,只有true
的布尔才有效,所以我把它转过来包括where.not
Lead.includes(:contacts).where.not("contacts.primary" => true).first
这完美无缺