使用MySQL檢索每個群組的最後一筆記錄
P粉736935587
2023-08-20 11:48:53
<p>有一個名為<code>messages</code>的表,其中包含如下所示的資料:</p>
<pre class="brush:php;toolbar:false;">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</pre>
<p>如果我執行查詢<code>select * from messages group by name</code>,將會得到以下結果:</p>
<pre class="brush:php;toolbar:false;">1 A A_data_1
4 B B_data_1
6 C C_data_1</pre>
<p>哪個查詢會傳回以下結果? </p>
<pre class="brush:php;toolbar:false;">3 A A_data_3
5 B B_data_2
6 C C_data_1</pre>
<p>也就是說,每個組中的最後一筆記錄應該會被回傳。 </p>
<p>目前,這是我使用的查詢:</p>
<pre class="brush:php;toolbar:false;">SELECT
*
FROM (SELECT
*
FROM messages
ORDER BY id DESC) AS x
GROUP BY name</pre>
<p>但這看起來效率很低。有其他方法可以達到相同的結果嗎? </p>
UPD: 2017-03-31,MySQL的版本5.7.5預設啟用了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的解決方案已經在我的戴爾e4310上運行了幾個小時,我不知道它何時會完成,儘管它在覆蓋索引上操作(因此在EXPLAIN中顯示
using index
)。我還有幾個基於相同思路的解決方案:
group_id
中的最大(group_id, item_value)
對就是每個group_id
的最後一個值,如果我們按降序遍歷索引,則是每個group_id
的第一個值;3 ways MySQL uses indexes是一篇很好的文章,可以了解一些細節。
解決方案1
#這個解決方案非常快,對於我1800萬 行的數據,大約需要0.8秒:
如果要改變順序為升序,將其放入子查詢中,只傳回ID,並將其作為子查詢與其他列連接:
對於我的數據,這個解決方案大約需要1.2秒。
解決方案2
#這是另一個解決方案,對於我的表,大約需要19秒:
它也按降序傳回測試結果。它的速度較慢,因為它進行了完整的索引掃描,但是它可以給你一個關於如何為每個組輸出N個最大行的想法。
該查詢的缺點是它的結果無法被查詢快取。
MySQL 8.0現在支援視窗函數,幾乎所有流行的SQL實作都支援。使用這種標準語法,我們可以寫最大-n-per-group查詢:
MySQL手冊中展示了此方法及其他尋找分組最大行的方法。
以下是我在2009年為這個問題寫的原始答案:
我這樣寫解決方案:
關於效能,根據資料的性質,其中一個解決方案可能會更好。因此,您應該測試兩個查詢,並根據資料庫的效能選擇更好的查詢。
例如,我有一個StackOverflow八月資料轉儲的副本。我將用它進行基準測試。在
Posts
表中有1,114,357行資料。這是在我的Macbook Pro 2.40GHz上運行的MySQL 5.0.75。我將編寫一個查詢來尋找給定用戶ID(我的)的最新貼文。
首先使用了Eric在子查詢中使用
#GROUP BY
的技術:即使
EXPLAIN
分析也需要超過16秒:現在使用
#LEFT JOIN
使用我的技術產生相同的查詢結果:EXPLAIN
分析顯示兩個資料表都能使用它們的索引:這是我的
Posts
表格的DDL:評論者註意:如果您想要使用不同版本的MySQL、不同的資料集或不同的表設計進行另一個基準測試,請隨意自行進行。我已經展示了上述技術。 Stack Overflow的目的是要向您展示如何進行軟體開發工作,而不是為您完成所有工作。