有一個表格messages
,其中包含的資料如下所示:
Id Name Other_Columns ------------------------- 1 A A_data_1 2 A A_data_2 3 A A_data_3 4 B B_data_1 5 B B_data_2 6 C C_data_1
如果我執行查詢 select * from messages group by name
,我將得到的結果為:
1 A A_data_1 4 B B_data_1 6 C C_data_1
什麼查詢將傳回以下結果?
3 A A_data_3 5 B B_data_2 6 C C_data_1
也就是說,應該傳回每組中的最後一筆記錄。
目前,這是我使用的查詢:
SELECT * FROM (SELECT * FROM messages ORDER BY id DESC) AS x GROUP BY name
但這看起來效率很低。有其他方法可以達到相同的結果嗎?
UPD:2017-03-31,版本5.7.5 MySQL 預設啟用 ONLY_FULL_GROUP_BY 開關(因此,非確定性 GROUP BY 查詢已停用)。此外,他們更新了 GROUP BY 實現,即使禁用了開關,該解決方案也可能無法按預期工作。需要檢查一下。
當組內的項目數相當小時,Bill Karwin 的上述解決方案工作正常,但當組相當大時,查詢的性能會變得很差,因為該解決方案需要大約n*n/2 n/ 2 僅進行
IS NULL
比較。我在包含
18684446
行和1182
群組的 InnoDB 表上進行了測試。此表包含功能測試的測試結果,並以(test_id, request_id)
作為主鍵。因此,test_id
是一個群組,我正在為每個test_id
搜尋最後一個request_id
。Bill 的解決方案已經在我的 Dell e4310 上運行了幾個小時,儘管它在覆蓋索引上運行(因此在 EXPLAIN 中使用索引),但我不知道它什麼時候會完成。
我有幾個基於相同想法的其他解決方案:
(group_id, item_value)
對是每個group_id
中的最後一個值,即如果我們按降序遍歷索引,則為每個group_id
的第一個;MySQL 使用索引的 3 種方式 是一篇很棒的文章,可以幫助您了解一些細節。
解決方案1
#這個速度快得令人難以置信,在我的 18M 行上大約需要 0.8 秒:
如果您想將順序變更為 ASC,請將其放入子查詢中,僅傳回 ids 並將其用作子查詢來連接其餘列:
這對我的數據來說大約需要 1.2 秒。
解決方案2
#這是另一個解決方案,對於我的表來說大約需要 19 秒:
它也按降序返回測試。它要慢得多,因為它執行完整索引掃描,但它可以讓您了解如何為每個群組輸出 N 個最大行。
該查詢的缺點是查詢快取無法快取其結果。
MySQL 8.0 現在支援視窗函數,例如幾乎所有流行的 SQL 實作。使用這個標準語法,我們可以寫出每組最大n個查詢:
此方法和其他尋找的方法分組最大行數在 MySQL 手冊中進行了說明。
以下是我在2009年針對這個問題寫的原始答案:
我這樣寫解決方案: