天天看點

SQL優化:組内排序取最大值

最近業務回報一個查詢異常的問題,需要DBA對查詢結果異常給出解釋,并幫助他們解決該問題。問題本質是一個組内排序取最大值的問題,根據業務需求,我建構了測試用例

查詢3這條SQL是我們需要讨論的内容,也是業務線為實作組内排序取最大值所采用的SQL。标準的程式員回報問題方式:XXX時間點之前查詢時正常的,這之後突然就不正常了,你們DBA是不是做什麼改動了?我把資料恢複到自己的測試機,傳回值也是正常的。暫且不去管姿勢是否正确,對這條SQL的分析,我們其實可以看出:(1)程式員期待group by執行結果是按照臨時表a的資料順序來取值;(2)程式員未考慮版本因素,資料量變化的因素;為此,我建構了上面的測試用例。

在不同版本的MySQL來進行測試:發現在Percona 5.5,Percona 5.1,MySQL 5.6關閉sql_mode= ONLY_FULL_GROUP_BY,MySQL5.1等版本下,傳回值确如程式員期待的順序,按照order by no desc的順序,相同name傳回no值最大的資料;

在mysql5.7,關閉sql_mode= ONLY_FULL_GROUP_BY和mariadb 10.*版本中,相同的name值,傳回則是取了最早寫入的資料行,忽略了order by no desc,按照資料的邏輯存儲順序來傳回;

其實在這裡,SQL等價于select id,no,name from testorder group by name。

這裡我們看出不同版本的傳回值是不同的,先擱置資料量的變化引起執行結果不同的讨論,因為資料量大小很難測試。

對上面的測試結果,在官方文檔上,有如下的參考

ONLY_FULL_GROUP_BY這個SQL_MODE出在mysql5.6(mariadb 10.0)時被引入,但本文讨論的内容和它無關,具體可以自己檢視文檔,這裡不做讨論。在5.6,5.5的官方文檔有相同的内容,Mariadb也有類似的解釋

并且,對from後的subquery子表中的order by也給出了解釋

好了,有了這些解釋,問題很明朗:

在from 後的subquery中的order by會被忽略

group by cloumn傳回的行是無序的

是以,業務獲得的正确的傳回值也是誤打誤撞。

那麼這個問題該怎麼解決?

在網上有一些SQL,很明顯不滿足需求,在這裡做一下展示,希望同學們避免被誤導:

錯誤SQL集合

我們可以這樣寫,雖然效率不高

或者這樣