1. 程式人生 > >提升統計頁面獲取資料效率的實現思路

提升統計頁面獲取資料效率的實現思路

ROR專案中經常會有統計頁面的需求。統計頁面由於資料量和運算量較大,因此不同的資料獲取計算方法,可能對頁面載入的速度有極大的影響(可能載入時間會差10倍以上)。下面分享一下我最近用過的一種方法(其實應該說是思路)

應用場景——比如我需要統計全國各個省份下,在指定時間範圍內,所有訂單的狀況(未付款、已付款、已收貨、已評價)

解決思路——
早期我可能會用比較笨的方法,先將31個省份用each迭代輸出,然後每行再根據省份,去資料庫裡查詢省份==當前省份,狀態等於指定狀態的訂單的總數。這麼算下來,先對省份表進行了一次查詢,然後31個省份,每個省份針對四種不同的狀態,各查詢了四次,總計對資料庫進行了125次查詢。這還是省份數量和狀態數量不多的情況。這樣做,程式碼寫起來雖然很簡單,但付出的代價是非常大的,當資料量大到一定程度時,可能會出現頁面載入超時而報錯的結果。

現在,我說一下我的解決思路:對於這類的統計,推薦用sql語句一次性從資料庫裡取出所有需要用到的結果,然後按照一定規則,將結果推入一個雜湊中,最後再view裡合理呼叫這個雜湊進行輸出。拿上面的例子來說,具體的實現方法是這樣的:

1.取資料

sql = "select o.province_id, o.status, count(o.id) as total_num from orders o where o.created_at >= 'xxxx-xx-xx' and o.created_at <= 'xxxx-xx-xx' group by o.province_id, o.status"
search  = Order.find_by_sql([sql])

2.建立一個存結果的hash

@result = {}
seach.each do |s|
  @result["#{s.province}_#{s.status}"] = s.total_num
end
上面這些程式碼我大致解釋一下:先通過資料庫的group by方法,把需要的統計結果,按照省份和訂單狀態的維度進行切割,並取出。然後通過這兩個引數來往@result這個空的雜湊裡推送資料,理論上,每個省份會有四條資料(因為可能為空)
然後我們再一次性從province表中取出需要用到的省份資訊
@provinces = Province.all.map{|p| [p.id, p.name] }

3.前臺view裡輸出

假設order表的status欄位用了列舉: Status = {unpay: 1, payed: 2, received: 3, commented: 4},我們用Oreder::Status.values可以直接獲得一個[1, 2, 3, 4]陣列
%table
  %tr 
    %th 省份 
    %th 未付款
    %th 已付款
    %th 已收貨
    %th 已評價
  @provinces.each do |p|
    %tr
      %td= p[1]#這個就是省份的名稱,因為@provinces是一個數組,陣列的每個元素又是一個數組
      Order::Status.values.each do |s|
        %td= @result["#{p[0]}_#{s}"] || 0
#上面這一句是view最重要的方法,根據在controller裡推送進@result雜湊裡的資料的命名規則,調出這些資料,如果沒有被呼叫到,說明該時間段內,該省份在當前狀態下,沒有訂單,因此如果為空,則補一個0即可。
#至此整個統計頁面都完成了,可以看到,首先,程式碼量非常少,簡潔易懂。其次,整個統計只對資料庫進行了兩次查詢,並且在view裡也沒有逐項呼叫子方法去計算結果,而是簡單的利用.each來輸出雜湊裡的值,因此整個頁面對系統的產生的負擔非常小,頁面載入速度也非常快,幾乎都是秒開。