Rails:在已经左手连接的桌子上急切加载?

时间:2018-01-25 18:23:31

标签: ruby-on-rails ruby-on-rails-4 activerecord rails-activerecord

如果关系存在,我们已经离开了一个表,以便我们可以按列排序:

people = Person
  .joins("LEFT JOIN addresses ON addresses.id = people.address_id")
  .order("addresses.country")
  .all

这导致单个SQL查询,但我希望people.first.address不会触发SQL来加载地址。我是离开加入的,因为有些人没有地址。

.includes(:address)会触发单独的查询。

您可以使用includes执行我使用内部联接建议的内容,但会触发2个SQL查询:

Person.includes(:address).all

虽然joins + includes只触发一个(但是INNER联接):

Person.joins(:address).includes(:address).all

如果在急切加载时强制加入,则活动记录也会使用左联接  .eager_load(:addresses)

您是否可以使用现有的左连接并使用这些结果加载轨道?到目前为止,我无法找到它。

3 个答案:

答案 0 :(得分:3)

试试这个:

people = Person.
  eager_load(:address).
  merge(Address.order("coalesce(country, '')")).
  all

people.first.address

eager_load通过执行LEFT OUTER JOIN强制加载。

我在国家/地区添加了合并,因此您可以更好地控制没有地址的人在结果中出现的位置。

以下是我的看法:

people = Person.
  eager_load(:address).
  merge(Address.order("coalesce(country, '')")).
  all

(0.5ms)  SELECT DISTINCT COUNT(DISTINCT "people"."id") FROM "people" LEFT OUTER JOIN "addresses" ON "addresses"."person_id" = "people"."id"
SQL (2.1ms)  SELECT "people"."id" AS t0_r0, "people"."name" AS t0_r1, "people"."created_at" AS t0_r2, "people"."updated_at" AS t0_r3, "addresses"."id" AS t1_r0, "addresses"."person_id" AS t1_r1, "addresses"."address" AS t1_r2, "addresses"."country" AS t1_r3, "addresses"."created_at" AS t1_r4, "addresses"."updated_at" AS t1_r5 FROM "people" LEFT OUTER JOIN "addresses" ON "addresses"."person_id" = "people"."id" ORDER BY coalesce(addresses.country, '')


people.first.address
nil

people.last.address
#<Address:0x007febabb508a8> {
            :id => 1,
     :person_id => 4,
       :address => "24175 Gerhold Prairie",
       :country => "O",
    :created_at => Thu, 01 Feb 2018 18:47:45 UTC +00:00,
    :updated_at => Thu, 01 Feb 2018 18:47:45 UTC +00:00
}

请注意,访问地址时不会运行任何查询

我指出你已经通过一个离开外部的表来命令,所以你需要决定如何处理空值。

答案 1 :(得分:2)

Well, in your case you could select your query creating an alias for the fields you are going to use from address something like this:

people = Person
         .joins("LEFT JOIN addresses ON addresses.id = people.address_id")
         .select("people.*, addresses.country as address_country")
         .order("address_country")
         .all

This won't change your desired query and will not result in extra queries, not for country at least.

答案 2 :(得分:1)

您可以#references#includes LEFT JOIN使用people = Person .includes(:addresses) .references(:addresses) .order("addresses.country") 您的关系,并实现您的目标。

#references

这将产生一个查询和所有人,无论他们是否有地址,并且还急于加载地址以避免可怕的N + 1查询。

文档在解释LEFT JOIN将添加WHERE方面做得不是很好,但其目的是允许添加SQL子句(ORDER,结合GROUP的关系#includesViewController等)。