2014-02-14 43 views
2

我試圖在Rails 3和4中使用explain方法來估計可能是特別昂貴的查詢返回的行數。它加入了3個表,並且可能導致1000萬行表的表掃描,這與count()聚合特別慢(數據庫是Postgres 9.3)。ActiveRecord :: Relation#explain總是運行查詢

我的問題是這樣的。如果我使用內置的explain()方法,那麼在返回結果之前,查詢總是在幕後運行。這可能需要2分鐘以上。可能有其他情況下,我想分析的查詢可能需要幾小時才能運行(例如,用於報告)。

我有一個稍微醜陋的解決方案,我做了一個to_sql,在前面加上「解釋」,然後執行查詢。這在Rails 3中工作,但需要Rails 4的一些返工。

所以我想我的問題是這樣的。有沒有辦法讓內建的AR explain()方法去做我想做的事情,還有其他一些優雅的方式來做到這一點,或者這是AR :: explain()中的一個錯誤,需要在某些地方記錄和修復點?

+1

我不相信鐵軌這樣做(並且不能關閉選項) – OneChillDude

回答

2

這裏是我是如何做到這一點。 在Rails 3和4中,我爲ActiveRecord :: Relation編寫了一個初始化程序。

首先,在Rails 3中:

class ActiveRecord::Relation 
    HUGE_COUNT = 20000 

    def count(column_name = nil, options = {}) 
    exact, has_conditions = false, false 
    h = (column_name.class == Hash ? column_name : options) 
    exact = h[:exact] 
    has_conditions = h[:conditions] 
    has_distinct = (column_name.class == String) && (column_name =~ /\bdistinct\b/i) 
    h = h.except(:exact) # Remove it because super won't understand it 
    column_name.class == Hash ? column_name = h : options = h 
    if exact || has_conditions || has_distinct 
     super 
    else 
     est = estimated_count 
     est > HUGE_COUNT ? est : super 
    end 
    end 

    def estimated_count 
    node = connection.execute("EXPLAIN #{self.to_sql}").first 
    match = node['QUERY PLAN'].match(/rows=\d+\b/) 
    match ? match[0].split('=').last.to_i : 0 
    end 

軌道4,5是除了一樣:

def estimated_count 
    node = {} 
    connection.unprepared_statement do 
     node = connection.execute("EXPLAIN #{self.to_sql}").first 
    end 
    match = node['QUERY PLAN'].match(/rows=\d+\b/) 
    match ? match[0].split('=').last.to_i : 0 
    end 

因爲到目前爲止,我發現,這是HUGE_COUNT低一般非常精確到1%或2%以內。這對我的需要很好,但顯然這是相當危險的......

1

我不確定是否有一種方法可以異步執行此操作。不過,您可以使用resquesidekiq異步運行您的查詢。

這裏的鏈接到resque:

https://github.com/resque/resque

這裏的鏈接到sidekiq:

https://github.com/mperham/sidekiq

+0

謝謝Rico,但我真的需要做到這一點同步。我研究瞭如何使用to_sql來做到這一點,所以我會寫出我自己的答案。 – user2259664

相關問題