2017-03-02 64 views
1

我有一個系統和報表模型。系統has_many報告和報告belongs_to系統。每份日報包含每個系統175條記錄。索引查詢優化頁面

我需要在我的系統#索引頁面上查詢,該頁面應列出在最新報告創建時過濾的所有系統。這是我第一次嘗試。

@systems = System.joins('LEFT JOIN reports ON reports.system_id = systems.id').group('systems.id').order('MAX(reports.created_at) ASC') 

此列出系統的報告(系統負載(2.1ms)),但是SYSTEM_ID排序不報告created_at。

第二次嘗試

@systems = System.joins(:reports).where("reports.created_at = (SELECT MAX(created_at) FROM reports p group by system_id having p.system_id = reports.system_id)").order('reports.created_at DESC') 

這個查詢做工作,但實在是太慢了(系統負載(546.2ms)),儘管有上report.created_at的索引。

第三次嘗試

@systems = System.joins(:reports).where("reports.id = (SELECT MAX(id) FROM reports p group by system_id having p.system_id = reports.system_id)").order('reports.id DESC') 

也做了工作,略高於第二次嘗試(系統負載(468.3ms))快,但仍然不夠快。

任何提示?

編輯03032017

我做了一個小的測試數據集

舊的查詢

SELECT s.* FROM systems s 
JOIN reports r ON r.system_id = s.id 
WHERE r.created_at = (
    SELECT MAX(created_at) 
    FROM reports p 
    group by p.system_id 
    having p.system_id = r.system_id) 
ORDER BY r.id DESC 

Time: 622.683 ms 

菲利普·庫壽齡解決方案的數字(乾淨的,僅返回與報告系統)

SELECT systems.* 
FROM systems 
JOIN (
    SELECT reports.system_id 
    , MAX(reports.created_at) created 
    FROM reports 
    GROUP BY reports.system_id 
) AS r_date ON systems.id = r_date.system_id 
ORDER BY r_date.created; 

Time: 1.434 ms 

BookofGr如解決方案(將給我所有的系統,報告或沒有報告)

select systems.* from systems order by updated_at; 

Time: 0.253 ms 

我無法得到systemjack的解決方案的工作。

最快的解決方案:bookofgreg

乾淨的解決方案:菲利普·庫壽齡

感謝您的輸入。

回答

0

時間緩存列上(reports.system_id, reports.created_at)索引可能,使這項工作有效:

@systems = System.joins(:reports).where("reports.created_at = (SELECT MAX(created_at) FROM reports p where p.system_id = reports.system_id) system_id)").order('reports.created_at DESC') 

另類...

你的第二張代碼:

System.joins(:reports).where("reports.id = (SELECT MAX(id) FROM reports p group by system_id having p.system_id = reports.system_id)").order('reports.id DESC') 

擴展爲:

SELECT system.* 
    JOIN reports ON system.id = reports.system_id 
    WHERE reports.created_at = (
          SELECT MAX(created_at) 
          FROM reports p 
         group by p.system_id 
          having p.system_id = reports.system_id) 
         ) 
ORDER BY reports.id DESC 

注意它如何在報告看兩次。另外,因爲您包含p.system_id = reports.system_id),所以每個系統記錄將調用一次嵌套查詢。

理想情況下,你想獲得system_ids和報告日期的列表: 所以...

SELECT reports.system_id 
     , MAX(reports.created_at) created 
     FROM reports 
    GROUP BY reports.system_id 

然後再加入到:

SELECT systems.* 
    FROM systems 
    JOIN (
      SELECT reports.system_id 
       , MAX(reports.created_at) created 
      FROM reports 
     GROUP BY reports.system_id 
     ) AS r_date ON systems.id = r_date.systems_id 
ORDER BY r_date.created 
+0

對此回覆非常滿意,感謝您的解釋。明天會試試看。 –

0

如果您不需要頁面上的報告數據,則一個可能的解決方案是在更新時報告after_save -> { self.system.touch } # in Report。這將導致系統的updated_at在報告更新之前進行。

這意味着您可以按照更新的系統對系統進行排序,而無需加入。

此解決方案假定沒有其他方式來更新系統。如果有,那麼你可以指定你可以用它來訂購像after_save -> { self.system.touch(:report_cached_updated_at) }

http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-touch

+0

非常感謝,這看起來很有希望。我在我的視圖中引用了報表數據,但僅引用了最後一個報表日期,在此解決方案中與system_updated相同。 –

+0

沒問題:)上週在has_many:through關係中解決了這個問題,這個關係想要顯示最近更新的東西的狀態。 記得接受它是否適合你,祝你好運! – BookOfGreg

0

一個window function可能給您帶來不錯。不知道如何實現這種在軌,但查詢以獲得每個系統的最新報告可能看起來像:

select * from (
    select s.*, r.sytem_id, r.created_at, 
     row_number() OVER (PARTITION BY s.id ORDER BY r.created_at desc) AS row 
    from systems s 
    left join reports r on r.system_id = s.id 
) where (row = 1 OR r.system_id is null) 

爲空的檢查是存在的,因爲你有一個左連接在你的榜樣,所以你必須要系統即使沒有報告。

或簡單(但不是肯定的語法是正確的):

​​
+0

欣賞您的輸入。從技術角度來看,我認爲Philip Couling和你的答案是最純粹的。如果我看性能增益,我傾向於BookofGreg。每日自動報告導入來自第三方API幷包含aproximatley 87000記錄,新系統會自動添加到(靜態)系統表中。所以我認爲完全避免加入可能最適合我的需求:-) –