[英]Rails Associations has_one Latest Record
我有以下模型:
class Section < ActiveRecord::Base
belongs_to :page
has_many :revisions, :class_name => 'SectionRevision', :foreign_key => 'section_id'
has_many :references
has_many :revisions, :class_name => 'SectionRevision',
:foreign_key => 'section_id'
delegate :position, to: :current_revision
def current_revision
self.revisions.order('created_at DESC').first
end
end
其中current_revision
是最近创建的revision
。 是否可以将current_revision
转换为关联,以便我可以执行类似Section.where("current_revision.parent_section_id = '1'")
? 或者我应该在我的数据库中添加一个current_revision
列,而不是尝试以虚拟方式或通过关联来创建它?
要获得 has_many 的最后一个,您需要执行类似于 @jvnill 的操作,除了添加一个对关联进行排序的范围:
has_one :current_revision, -> { order created_at: :desc },
class_name: 'SectionRevision', foreign_key: :section_id
这将确保您从数据库中获得最新的修订版本。
您可以将其更改为关联,但通常情况下,对has_one
或belongs_to
关联的排序在用于查询时总是被错误地解释。 在你的问题中,当你把它变成一个关联时,那就是
has_one :current_revision, class_name: 'SectionRevision', foreign_key: :section_id, order: 'created_at DESC'
问题在于,当您尝试将其与其他查询结合使用时,它通常会给您错误的记录。
>> record.current_revision
# gives you the last revision
>> record.joins(:current_revision).where(section_revisions: { id: 1 })
# searches for the revision where the id is 1 ordered by created_at DESC
所以我建议你添加一个current_revision_id
来代替。
正如@jvnill 提到的,使用order
解决方案在进行更大的查询时停止工作,因为order
的范围是完整的查询而不仅仅是关联。
这里的解决方案需要准确的SQL:
has_one :current_revision, -> { where("NOT EXISTS (select 1 from section_revisions sr where sr.id > section_revisions.id and sr.section_id = section_revisions.section_id LIMIT 1)") }, class_name: 'SectionRevision', foreign_key: :section_id
我知道您想要获取每个部分的最后一个修订版具有 parent_section_id = 1 的部分;
我有类似的情况,首先,这是 SQL(请将类别视为您的部分,将帖子视为修订,将 user_id 视为 parent_section_id -对不起,如果我没有将代码移至您的需要,但我必须去):
SELECT categories.*, MAX(posts.id) as M
FROM `categories`
INNER JOIN `posts`
ON `posts`.`category_id` = `categories`.`id`
WHERE `posts`.`user_id` = 1
GROUP BY posts.user_id
having M = (select id from posts where category_id=categories.id order by id desc limit 1)
这是 Rails 中的查询:
Category.select("categories.*, MAX(posts.id) as M").joins(:posts).where(:posts => {:user_id => 1}).group("posts.user_id").having("M = (select id from posts where category_id=categories.id order by id desc limit 1)")
这是有效的,它很丑,我认为最好的方法是“剪切”查询,但是如果您有太多的部分,那么在循环它们时就会出现问题; 您也可以将此查询放入静态方法中,而且,您的第一个想法,在部分表中有一个 revision_id 将有助于优化查询,但会放弃规范化(有时需要),您将不得不在为该部分创建新修订时更新此字段(因此,如果您要在庞大的数据库中进行大量修订,如果服务器速度较慢,这可能是个坏主意……)
更新我回来了,嘿嘿,我正在做一些测试,看看这个:
def last_revision
revisions.last
end
def self.last_sections_for(parent_section_id)
ids = Section.includes(:revisions).collect{ |c| c.last_revision.id rescue nil }.delete_if {|x| x == nil}
Section.select("sections.*, MAX(revisions.id) as M")
.joins(:revisions)
.where(:revisions => {:parent_section_id => parent_section_id})
.group("revisions.parent_section_id")
.having("M IN (?)", ids)
end
我做了这个查询并使用了我的表(希望我命名了参数,它与之前的 Rails 查询相同,但我更改了查询以进行优化); 小心这群人; 包含使它在大型数据集中是最佳的,很抱歉,我找不到与 has_one 建立关系的方法,但我会这样做,但也会重新考虑您在开头提到的领域。
如果您的数据库支持DISTINCT ON
class Section < ApplicationRecord
has_one :current_revision, -> { merge(SectionRevision.latest_by_section) }, class_name: "SectionRevision", inverse_of: :section
end
class SectionRevision < ApplicationRecord
belongs_to: :section
scope :latest_by_section, -> do
query = arel_table
.project(Arel.star)
.distinct_on(arel_table[:section_id])
.order(arel_table[:section_id].asc, arel_table[:created_at].desc)
revisions = Arel::Nodes::TableAlias.new(
Arel.sql(format("(%s)", query.to_sql)), arel_table.name
)
from(revisions)
end
end
它适用于预加载
Section.includes(:current_revision)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.