鉴于这两个模型
class Article < AR::Base
belongs_to :tag
default_scope order('id DESC')
end
class Tag < AR::Base
has_many :articles
scope :featured, order(:ordering).limit(5)
end
我正在尝试以这样一种方式加入这两个表:我可以检索特色标签列表,并为每个表添加该标签中带有单个查询的最新文章,例如
Tag.featured.each do |tag|
p tag.name
p tag.articles.first.title # this will fetch the article in the tag with the highest id
end
这样编写的代码有(n+1) query problem,我正在尝试优化它,因为它会非常频繁地运行。我在includes
范围内排除了featured
来电,因为它会在前5个标记中加载所有Article
(很多文章......)
在普通的旧SQL中,我可以使用像这样的查询来连接这两个表
SELECT tag_id, MAX(id) FROM articles GROUP BY tag_id
# or in Rails
Article.select('MAX(id), tag_id').group(:tag_id)
+--------+---------+
| tag_id | MAX(id) |
+--------+---------+
| 14 | 26787 |
...
| 1 | 27854 |
| 5 | 27780 |
| 0 | 10953 |
+--------+---------+
作为连接表,我可以使用单个查询检索所有数据。
如何在Rails和ActiveRecord中移植它?
更新
我需要在AR上下文中执行的完整查询是
SELECT a.*, t.*
FROM tags t
JOIN (SELECT MAX(id) AS latest, tag_id FROM articles GROUP BY tag_id) j ON j.tag_id = t.id
JOIN articles p ON j.latest = a.id
LIMIT 5
我尝试使用最新查询的AR#find_by_sql
方法,我得到了正确的结果集但是我无法浏览对象
sql = '...' # This holds the previous query
Tag.find_by_sql(sql).first.class
=> "Tag"
Tag.find_by_sql(sql).first.articles.first
=> nil # why???
更新2
也尝试了
Tag.joins('JOIN ... all my query join part here ...').first.articles.first
=> nil
但是我注意到我可以直接将我的文章字段用作标记字段,即我可以写
Tag.find_by_sql(...).first.title # where title is a field of Article class
Tag.joins(...).first.title # where title is a field of Article class
但显然我无法调用Article
实例方法。
答案 0 :(得分:0)
找到了我的问题的部分解决方案。我可以通过两个查询获取所需的记录,并且仍然将数据作为AR对象加载:
# This will fetch the needed article ids
ids = Article.select('MAX(id) as max_id').group(:tag_id).map(&:max_id)
# This return the top tags each one with the article needed in articles.first
Tag.includes(:articles).where('article.id IN (?)', ids).limit(5).each do |t|
t.name # gives the tag name
t.articles # gives [<#Article...>] i.e. an array with a single article
end
Rails将执行三个查询:
Tags
ID LEFT OUTER JOIN
作为条件WHERE a.id IN (ids) AND t.id IN (returned_ids)
获取最新信息来从两个表中获取数据
醇>
我不能使用像
这样的WHERE条件where('article.id IN (SELECT MAX(id) from articles)')
因为MySQL has a bug and it would think the subquery is derived。它似乎是will be fixed in 6.0。
仍在寻找更好的答案,也许是一个JOIN,例如我的问题中加载AR对象的那个。