2015-03-02 4 views
2

Я использую 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_load: Case.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 объектов.

ответ

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: область объединения «last_note» зависит от экземпляра (блок области принимает аргумент). Предварительная загрузка происходит до создания отдельных экземпляров. Это означает, что экземпляр ассоциации не передается экземпляру. Это, скорее всего, приведет к нарушению или неправильному поведению. Присоединение, предварительная загрузка и интенсивная загрузка этих ассоциаций устарели и будут удалены в будущем.

Кроме того, этот способ не работает с preload (отдельным запросом SQL)

+0

избежать предупреждений с помощью объединения вместо экземпляра-зависимый, где положение: 'has_many: birthday_events, -> {присоединяется («JOIN events on events.starts_on = users.birthday»}, class_name: «Событие» – s2t2

Смежные вопросы