2013-03-20 27 views
0

我一整天都在試圖重構我的查詢。需要花費幾分鐘的時間才能載入20個記錄。起初,我責備查詢這個計算器後:在rails中重構一個昂貴的查詢

rails - using Rails.cache gives error

然而,做了一些測試後,我意識到,查詢被加載速度非常快,並且是不是罪魁禍首。

然後,我責備它雷,因爲我認爲這是雷填充幾千條記錄到Ruby對象,因爲我在這個崗位解釋說:

rails and kaminari

但是,這是錯誤的了。 Kaminari一次只加載20條記錄(我使用記錄器來檢查加載的報告數量,並顯示20條記錄)。

所以最後我想我找到了真正的罪魁禍首。這是在該頁面分頁期間發生的另一個查詢。我意識到,這個查詢花費超過20秒加載每次運行時間:

def virtual_fence_duration 
    inside_fence_time = nil 
    outside_fence_time = nil 

    outside_fence_time = self.time 

    previous_reports = Report.where{(time < my{self.time}) & 
            (unit_id == my{self.unit_id})}.order('time desc') 

    previous_reports.each do |report| 
     alerts = report.alerts.collect { |a| a.code_name } 
     if alerts.include? "Inside virtual fence" 
     inside_fence_time = report.time 
     break 
     end 
    end 

    if inside_fence_time && outside_fence_time 
     "Virtual Fence Elapsed Time: #{(((outside_fence_time - inside_fence_time).to_i).to_f/60.0).ceil} minutes" 
    else 
     "" 
    end  
    end 

基本上,這種方法被調用時,有一個警示,即「外虛擬圍欄」。我儲存時間。然後我查詢它之前的所有報告(報告has_many警報)。然後,我使用ruby的每個迭代器遍歷每個報告,然後使用與這些報告相關的警報來查找「在虛擬圍欄內」的警報。然後我將這個報告的時間存儲起來,並把這兩個時間區別開來。如果在兩個持續時間之間有很多報道,這個邏輯似乎是永久的。有沒有更有效的方法來做到這一點在SQL(MySQL)或紅寶石?

+0

那'where'語法不是標準的ActiveRecord,你使用squeel嗎?另外,是'報警'報告的一個'has_many'關聯? – PinnyM 2013-03-20 21:15:45

+0

@PinnyM是的,它似乎正在使用squeel。如果它導致性能問題,那麼我不介意從查詢中刪除它。是的一個報告has_many警報 – JohnMerlino 2013-03-20 21:18:52

回答

0

這似乎是一個N + 1性能問題。使用includes嘗試預先加載:

previous_reports = Report.where{(time < my{self.time}) & 
           (unit_id == my{self.unit_id})}. 
          includes(:alerts).order('time desc') 

這肯定應該至少有一定的影響,但仍可能是緩慢取決於排在你的表數。如果您對此不滿意,則需要將索引添加到reports表的timeunit_id列中,並檢查alerts表的report_id列中是否有索引。

您也可以在單個查詢中執行所有這些邏輯,在當前方法中應該有一個顯着改進。當然,有正確的指數也不會傷害:

prev_report = Report.joins(:alerts) 
       where{time < my{self.time} && 
         unit_id == my{self.unit_id} && 
         alerts.code_name.like '%Inside virtual fence%'}. 
       order("reports.time DESC").first 

inside_fence_time = prev_report.time if prev_report 
+0

不使用該squeel也改善了性能。我的最終查詢:報告。 加入({:alerts =>:alert_code})。 其中(:unit_id => unit_id)。 其中(「reports.time <?」,time)。 其中(「alert_codes.name =?」,「內部虛擬圍欄」)。 order(「reports.time DESC」)。第一個 – JohnMerlino 2013-03-21 14:46:36

+0

只是一個與此有關的簡短問題。正如你所看到的,那個查詢hasa加入了呼叫,兩個呼叫和一個命令呼叫。我應該使用範圍來使這更優雅,而不是像這樣的一段長長的代碼? – JohnMerlino 2013-03-21 14:48:26

+0

@JohnMerlino:如果你在幾個地方使用這些查詢優化,那麼通過一切手段來構建一個範圍。 – PinnyM 2013-03-21 14:51:44

0

首先我不熟悉在mysql中使用"=="的查詢。它應該只是"="。 其次,查詢中使用的"my()"函數是什麼?

除非我不理解你的問題,或者你正在做的高級事情,否則我會認爲類似這樣的查詢會讓你找到你想要的東西。

Report.where('time < ? and unit_id = ?', self.time, self.unit_id).order('time desc') 

如果不是,則發佈實際從日誌文件運行的查詢。

+0

在閱讀其他評論後,我看到你正在使用squeel。我對此一點都不熟悉,所以我的回答是基於積極的記錄。 – Catfish 2013-03-20 21:20:45

+0

OP使用的是[squeel gem](https://github.com/ernie/squeel),它基本上是一個針對SQL的DSL解釋器 – PinnyM 2013-03-20 21:20:55