左边的子查询连接在rails中的关联范围

时间:2015-03-02 19:02:05

标签: ruby-on-rails ruby-on-rails-4 activerecord orm left-join

我将Rails 4.2.0与ActiveRecord一起使用。我有Case模型,has_many :notes

我的主要目标是在一个查询中获取每个案例的最新注释。注意字段应该是可排序的(例如ORDER BY notes.created_at)我可以通过这样的SQL查询实现这一点:

SELECT cases.*, notes.* FROM cases
LEFT OUTER JOIN notes ON cases.id = notes.case_id AND notes.id = (
  SELECT id FROM notes 
  WHERE notes.case_id = cases.id 
  ORDER BY notes.created_at DESC 
  LIMIT 1
)
ORDER BY notes.created_at DESC

我当前的case.rb代码如下:

has_one :latest_note, -> do
  where('id' => Note.where('"case_id" = "cases"."id"').order(created_at: :desc).limit(1))
end, inverse_of: :case, class_name: 'Note'

使用eager_load时,它工作正常:Case.eager_load(:latest_note).order('notes.created_at DESC')

SELECT "cases"."id" AS t0_r0, "cases"."created_at" AS t0_r1, "cases"."updated_at" AS t0_r2, "notes"."id" AS t1_r0, "notes"."case_id" AS t1_r1, "notes"."created_at" AS t1_r2, "notes"."updated_at" AS t1_r3
FROM "cases" LEFT OUTER JOIN "notes" 
  ON "notes"."case_id" = "cases"."id" 
  AND "notes"."id" IN (
    SELECT  "notes"."id" FROM "notes" 
    WHERE ("case_id" = "cases"."id")  
    ORDER BY "notes"."created_at" DESC 
    LIMIT 1
)  ORDER BY notes.created_at DESC

但是在没有SQLite3::SQLException: no such column: cases.id的情况下引发错误eager_loadCase.first.latest_note

SELECT  "notes".* FROM "notes" 
WHERE "notes"."case_id" = ? AND "notes"."id" IN (
  SELECT  "notes"."id" FROM "notes" 
  WHERE ("case_id" = "cases"."id")  
  ORDER BY "notes"."created_at" DESC 
  LIMIT 1
) LIMIT 1

P.S。有没有关联范围的解决方案吗?通过joins生成的左边连接不能与eager_load一起使用。没有eager_load它不会将数据库中的值映射到对象 - 日期保留为字符串,而不是DateTime个对象。

1 个答案:

答案 0 :(得分:2)

我刚刚找到了解决方法:

从我发现的documentation开始,该范围可以接受所有者对象作为参数:

class User < ActiveRecord::Base
  has_many :birthday_events, ->(user) { where starts_on: user.birthday }, class_name: 'Event'
end

所以,我的关系成了:

has_one :latest_note, ->(kase) do
  subquery = Note.order(created_at: :desc).limit(1)
  subquery = kase.is_a?(Case) ? subquery.where(case_id: kase.id) : subquery.where('"case_id" = "cases"."id"')
  where('id' => subquery)
end, inverse_of: :case, class_name: 'Note'

但是,Rails 4.2.0输出:

  

DEPRECATION WARNING:关联范围'latest_note'与实例有关(范围块采用参数)。预加载在创建单个实例之前发生。这意味着没有实例传递给关联范围。这很可能导致行为破裂或不正确。这些关联的加入,预加载和预先加载已弃用,将来会被删除。

此外,此解决方法不适用于preload(单独的SQL查询)