2013-04-22 68 views
2

比方說,你在你這樣的車型之一有ASSOCATION:高效的ActiveRecord關聯條件

class User 
    has_many :articles 
end 

現在假設你需要得到3個陣列,一個用於寫的文章昨天,寫的文章之一在過去的7天內,以及過去30天內撰寫的文章之一。

當然,你可以這樣做:

articles_yesterday = user.articles.where("posted_at >= ?", Date.yesterday) 
articles_last7d = user.articles.where("posted_at >= ?", 7.days.ago.to_date) 
articles_last30d = user.articles.where("posted_at >= ?", 30.days.ago.to_date) 

然而,這將運行3個獨立的數據庫查詢。更有效地,你可以這樣做:

articles_last30d = user.articles.where("posted_at >= ?", 30.days.ago.to_date) 
articles_yesterday = articles_last30d.select { |article| 
    article.posted_at >= Date.yesterday 
} 
articles_last7d = articles_last30d.select { |article| 
    article.posted_at >= 7.days.ago.to_date 
} 

現在當然這是一個人爲的例子,也沒有保證該陣列選擇實際上是比數據庫查詢速度更快,但讓我們姑且認爲它是。

我的問題是:有沒有什麼辦法(例如某些gem)以這種方式編寫代碼,通過確保您只需指定關聯條件來消除此問題,並且應用程序本身將決定是否需要執行另一個數據庫查詢或不?

ActiveRecord本身似乎沒有適當地覆蓋這個問題。您不得不每次查詢數據庫或將關聯視爲數組。

+0

你想擺脫什麼?額外的數組,額外的查詢,或只是打字? – Matzi 2013-04-22 11:56:56

+0

在我的實際代碼中,我通常不知道某個關聯是否已經加載。我想編寫一些不需要關心這個的代碼,並且仍然始終使數據庫查詢的數量達到最小。 – 2013-04-22 13:07:53

+0

我不認爲有任何現成的解決方案。這需要很大的努力來跟蹤所有條件和加載的數據。不要談論數據庫可以在幕後改變的事實。您需要手動創建最佳查詢。爲此,您可以使用技巧來簡化它。 – Matzi 2013-04-22 13:30:21

回答

0

有一對夫婦的方式來處理這個問題:

您可以爲您希望通過在關聯定義指定條件散列每個級別獨立的協會。然後,您可以簡單地爲您的用戶查詢加載這些關聯,並且您將針對整個操作使用db 3x,而不是每個用戶使用3x。

class User 
    has_many articles_yesterday, class_name: Article, conditions: ['posted_at >= ?', Date.yesterday] 
    # other associations the same way 
end 

User.where(...).includes(:articles_yesterday, :articles_7days, :articles_30days) 

你可以做一個小組。

什麼它歸結爲是你需要分析代碼並確定哪些將是最快的爲您的應用程序(或者,如果你甚至應該打擾它的話)

+0

不幸的是,這還不夠。我已經儘可能地加載,在這種情況下,只需要加載一個關聯,而不是三個,因爲這是一個性能關鍵的應用程序。 – 2013-04-22 14:07:27

0

可以擺脫的必要性用下面的代碼來檢查查詢。

class User 
    has_many :articles 

    def article_30d 
    @articles_last30d ||= user.articles.where("posted_at >= ?", 30.days.ago.to_date) 
    end 

    def articles_last7d 
    @articles_last7d ||= articles_last30d.select { |article| article.posted_at >= 7.days.ago.to_date } 
    end 

    def articles_yesterday 
    @articles_yesterday ||= articles_last30d.select { |article| article.posted_at >= Date.yesterday } 
    end 

end 

作用:

  • 使只有一個查詢最大值,如果有三個用於
  • 只計算使用的陣列,和30D版本在任何情況下,但只有一次

但是,即使您不使用它,它也不會簡化最初的30d查詢。夠了嗎,還是需要更多的東西?

+0

這實際上是我現在正在做的,上面的例子只是一個簡單的例子。 – 2013-04-22 16:24:05