2012-09-19 203 views
1

我有UserGift型號。用戶可以將禮物發送給其他用戶。我有一個關係表,告訴我哪些用戶收到了禮物。另一方面,用戶屬於School,可以免費或付費。使這個ActiveRecord查詢更有效率?

我想要在上週收到特定類型學校(這是免費或付費)禮物的用戶數。

我可以這樣做:

Gift.joins(:schools).where("created_at >= ? AND schools.free_school = ?", Time.now.beggining_of_week, true).collect(&:gift_recipients).flatten.uniq.count. 

或者說,我想知道有多少用戶發送禮物的最後一個星期。這工作:

Gift.joins(:schools).where("created_at >= ? AND schools.free_school = ?", Time.now.beggining_of_week, true).collect(&:user_id).uniq.count. 

如果我想知道有多少用戶發送或接收在上週的禮物,我可以這樣做:

(Gift.joins(:schools).where("created_at >= ? AND schools.free_school = ?", Time.now.beggining_of_week, true).collect(&:gift_recipients).flatten + Gift.joins(:schools).where("created_at >= ? AND schools.free_school = ?", Time.now.beggining_of_week, true).collect(&:user_id)).uniq.count 

所有這一切工作正常,但如果數據庫足夠大這真的很慢。你有什麼建議讓它更有效率,也許在需要時使用原始SQL?

"gifts" 
    user_id (integer) 
    school_id (integer) 
    created_at (datetime) 
    updated_at (datetime) 
"gift_recipients" is a table like 
    gift_id | recipient_id, 
+0

請張貼用戶,禮物和學校的模式/模型。 – Winfield

回答

2

你不想做到這一點使用收集(),它加載所有結果到內存和ActiveRecords的數組中篩選它們。這是緩慢和危險的,因爲它可能會泄漏/使用所有可用內存,具體取決於數據大小與服務器的大小。

一旦你發佈你的模式,我可以幫助你在SQL中查詢/聚合,這是正確的方法。

,而不是例如:

Gift.joins(:schools).where("created_at >= ? AND schools.free_school = ?", Time.now.beggining_of_week, true).collect(&:user_id).uniq.count 

你應該使用:

Gift.joins(:schools).where("created_at >= ? AND schools.free_school = ?", Time.now.beggining_of_week, true).count('distinct user_id') 

...這將在SQL算上不同user_ids並返回結果,而不是返回所有對象的並在記憶中計數。

+0

好.. 「禮物」 USER_ID(整數) 學校ID(整數) created_at(日期時間) 的updated_at(日期時間) 「gift_recipients」 就像 gift_id表| recipient_id,你是否需要更多信息?謝謝! – Johnny

+0

太棒了!我不知道你可以傳遞一個這樣的參數來計數..我在哪裏可以找到這樣的查詢的文檔? – Johnny

+0

http://api.rubyonrails.org/搜索計數() – Winfield

0

我看到這個老帖子,我想使一對夫婦的意見: 正如溫菲爾德說

Gift.joins(:school).where("created_at >= ? AND schools.free_school = ?", Time.now.beggining_of_week, true).count('distinct user_id') 

是這樣做的一個很好的方式。我會做

Gift.joins(:school).count('distinct user_id', :conditions => ["gifts.created_at >= ? AND free_school = ?", Time.now.beginning_of_week, true]) 

,但只是因爲這是更好的我的眼睛,個人的事情,你可以檢查兩個生產完全相同的SQL查詢。請注意,是有必要寫

gifts.created_at 

以避免歧義,因爲這兩個表有此名稱的列,列名

free_school 

的情況下,沒有歧義,因爲這不是列禮品表中的名字。對於第一個查詢我在做

Gift.joins(:school).where("created_at >= ? AND schools.free_school = ?", Time.now.beginning_of_week, true).collect(&:user_id).uniq.count 

這是尷尬。這效果更好

Gift.joins(:school).count("distinct user_id", :conditions => ["gifts.created_at >= ? AND free_school = ?", Time.now.beginning_of_week, true]) 

它避免了將禮物帶入內存並用ruby過濾它們的問題。

到此爲止,沒有什麼新的。關鍵在於我的問題是計算上週發送或接收禮物的用戶數量。爲此,我提出了以下

senders_ids = Gift.joins(:school).find(:all, :select => 'distinct user_id', :conditions => ['gifts.created_at >= ? AND free_school = ?', Time.now.beginning_of_week, type]).map {|g| g.user_id} 
    receivers_ids = Gift.joins(:school).find(:all, :select => 'distinct rec.recipient_id', :conditions => ['gifts.created_at >= ? AND free_school = ?', Time.now.beginning_of_week, type], :joins => "INNER JOIN gifts_recipients as rec on rec.gift_id = gifts.id").map {|g| g.recipient_id} 
    (senders_ids + receivers_ids).uniq.count 

我敢肯定存在這樣做的更好的辦法,我的意思是,在返回一個SQL查詢正是這個數字,但至少結果是數組只包含id的對象(r​​ecipient_id用於接收者的情況),而不是將所有對象都帶入內存。那麼這只是希望通過像我這樣的鋼軌來幫助新手進行sql查詢:)。