2011-09-07 55 views
2

加入鑑於這兩款車型Rails的,最大的條件

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,我試圖去優化它,因爲它會很頻繁地運行。我已經排除在featured範圍的includes調用,因爲它會加載前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

因爲沒有其他答案,我會自己接受我自己的答案。 – Fabio

回答

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會做三個查詢:

  1. 一個選擇IDS
  2. 一個選擇Tags ID爲
  3. 最近取得一個(很奇怪)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對象的問題。