2016-02-24 45 views
0

我在Rails中緩慢加載頁面時遇到了一些問題,我確定由於查詢數據庫(PostgreSQL)的方式。總結這種情況,我有三種模式 - 我們稱它們爲Model A,Model BModel C - 我插入表中,並且正在努力尋找一種有效的方式來查詢數據。在Rails中涉及三種模型的最快`哪裏'請求

Model A提供了表格的標題行Model B中的每一行tbody; Model C屬於Model AModel B並提供表格數據。

這裏有一個簡單的例子:

說出模型變量如下:

@model_a = ModelA.all 
@model_b = ModelB.all 
@model_c = ModelC.all 

相關如下:其中

class ModelA 

    has_many :model_cs 

end 

class ModelB 

    has_many :model_cs 

end 

class ModelC 

    belongs_to :model_a 
    belongs_to :model_b 

end 

視圖:

<table> 

    <thead> 
    <tr> 
     <th></th> 
     <!-- Model A provides the table headers --> 
     <% @model_a.each do |a| %> 
     <th> 
      <%= a.name %> 
     </th> 
     <% end %> 
    </tr> 
    </thead> 

    <tbody> 
    <!-- Each row represents a different instance of Model B --> 
    <% @model_b.each do |b| %> 
     <tr> 
     <td> 
      <%= b.name %> 
     </td> 
     <% @model_a.each do |a| %> 
      <td> 
      <!-- The actual table data is from Model C, when it belongs to both Model A and Model B --> 
      <% if (c = @model_c.where(model_a_id: a.id, model_b_id: b.id).first).present? %> 
       <%= c.name %> 
      <% end %> 
      </td> 
     <% end %> 
     </tr> 
    <% end %> 
    </tbody> 

</table> 

明顯這真的很慢,而且我會想象,很臭的代碼。我可以緩存片段,但是會更加快速地請求,以便在對數據進行任何更改後順利運行。很高興能考慮採用不同的方法 - 我無法想出一種以這種方式呈現數據的方式,而無需通過兩種模型來查詢第三種。

我敢肯定,還有很遠,更好的方法來解決這個問題,但還沒有拿出它。希望你們能夠提供幫助 - 事先感謝Steve。

+0

請編輯您的問題,包括定義'@ model_a'代碼,'@ model_b'和'@ model_c'。 –

+0

嗨喬丹 - 已經完成了。我還添加了他們相關的方式。謝謝。 – SRack

回答

1

在視圖中,該代碼將運行新的SQL查詢中的每個表格單元格:

@model_c.where(model_a_id: a.id, model_b_id: b.id).first 

所以這可能是什麼是真正傷害你。請記住,where是ActiveRecord的一部分並進入數據庫,而select對枚舉類型進行操作並在內存中執行所有操作。

您應該首先加載正確關係的所有內容。我會這樣開始:

@model_a = ModelA.all 
@model_b = ModelB.includes(:model_cs) 

那麼做到這一點:

<tbody> 
    <!-- Each row represents a different instance of Model B --> 
    <% @model_b.each do |b| %> 
    <tr> 
     <td> 
     <%= b.name %> 
     </td> 
     <% @model_a.each do |a| %> 
     <td> 
      <!-- The actual table data is from Model C, when it belongs to both Model A and Model B --> 
      <% if c = b.model_cs.select{|x| x.model_a_id = a.id}.first %> 
      <%= c.name %> 
      <% end %> 
     </td> 
     <% end %> 
    </tr> 
    <% end %> 
</tbody> 

這應該讓你在兩個或三個查詢一切。

請注意,如果A很大,select仍然執行不佳,因爲它執行線性搜索。所以,你可以在你這樣的ModelB類寫一個方法來代替,並在視圖中使用它:

def model_c_for(a) 
    @model_c_by_a ||= Hash[ 
    model_cs.map{|c| [c.model_a_id, c]} 
    ] 
    @model_c_by_a[a.id] 
end 
+0

非常感謝Paul的投入。這非常有意義,並且正是我期待的方法,*但它實際上減慢了約10秒的請求。不尋常的權利?我已經對這兩個查詢進行了基準測試,而且你的測試一直更快(約1.6s v 2.5s)。實現您的方法將ActiveRecord加載時間減少了十倍(5s - 500ms),但將視圖渲染增加了大約15s;與我期望看到的完全相反。你怎麼看?再一次,再次感謝您在這裏的幫助,我閱讀並認爲'是的,發現!'。 – SRack

+1

我假設你已經在所有外鍵列上有索引了? A和B有多大?如果B很大,請考慮分頁。如果A很大,請參閱我對答案的更新。如果A非常大,我會找到一種不會每次渲染所有列的方法,但讓用戶選擇他們想要查看的內容。 –

+1

此外,如果A和B都非常大,我會考慮繞過ActiveRecord這裏,只寫一個SQL查詢。只要您使用AR,將實例化所有這些ModelC將花費很多。由於您的列數不定(每個ModelA有一列),因此本文有幫助:http://illuminatedcomputing.com/posts/2013/03/fun-postgres-puzzle/ –