我在 Rails 应用程序中有以下架构
我的页面有很多加载时间(这是一个多态关联)。我想获取每个页面的最新 created_at
load_time,然后从中获取最大 load_time 字段编号(希望同名字段不会让您感到困惑)。这是我目前所拥有的,但使用了 ruby 方法:
pages.includes(:load_times).group(:resource_id).select("load_times.load_time, max(load_times.created_at) as date").reject{|lt| lt.load_time.nil?}.sort_by { |pg| pg.load_time.load_time }.last
如何将 reject
和 sort_by
ruby 方法转移到 sql land?
最佳答案
这个怎么样:
pages
.joins(:load_times)
.group(:resource_id)
.select("pages.*, load_times.load_time, MAX(load_times.created_at) as date")
.where.not(load_times: { load_time: nil })
.order("load_times.load_time")
.last
请注意,where.not
仅适用于 Rails >= 4。如果您使用的是 Rails 3,则可以将其替换为 where("load_times.load_time IS NOT NULL")
在评论中明确了想要的结果后,这是获取所有 load_time 最慢的 Page 对象的查询,仅考虑最近的 load_time
记录的 load_time。请注意,您通常可以在带有 MAX(load_times.created_at)
或带有 MAX(load_times.id)
的 having 子句中获得该结果,两者无法区分,后者更多一些高效的。如果这样做,请将所有对 created_at
的引用更改为 id
。
pages
.joins(:load_times)
.where.not(load_times: { load_time: nil })
.group('load_times.created_at')
.having('load_times.created_at = MAX(load_times.created_at)')
.order('load_times.load_time DESC')
.first
编辑
经过更多研究(是的,我个人认为 ;)),我终于让您的查询正常工作。您需要两次连接 load_times
表,使用连接条件中每个 Page
的最后一行。
此外,对于第二次连接,您需要使用单个列来标识 load_times
的最后一个条目。 created_at
可能无法正常工作,因为您的 load_times
表可能有两个具有相同 created_at
的条目。也许这不是你的情况,但以防万一我使用了 id
,因为它与 created_at
具有相同的顺序并且是唯一的。
此外,我删除了 where.not(load_times: { load_time: nil })
位,因为我假设您想要在其上获取最后一个 load_time
元素具有 load_time
属性。
最后,我假设了一个多态关联,因此 resource_type = 'Page'
。
这里是:
pages
.joins(:load_times)
.joins("INNER JOIN (SELECT l1.page_id, MAX(l1.id) AS max_id FROM load_times as l1 GROUP BY l1.page_id) l2 ON load_times.page_id = l2.page_id AND load_times.id = l2.max_id AND resource_type = 'Page'")
.order("load_times.load_time DESC")
.first
感谢发人深省的问题,学到了很多。
关于mysql - Activerecord:将 ruby 方法转移到 sql-land,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28609293/