Mysql order by與limit混用陷阱

2021-09-11 18:08:09 字數 2066 閱讀 4927

在mysql中我們常常用order by來進行排序,使用limit來進行分頁,當需要先排序後分頁時我們往往使用類似的寫法select * from 表名 order by 排序字段 limt m,n。但是這種寫法卻隱藏著較深的使用陷阱。在排序欄位有資料重複的情況下,會很容易出現排序結果與預期不一致的問題。

比如現在有一張user表,表結構及資料如下:

表結構

表資料現在想根據建立時間公升序查詢user表,並且分頁查詢,每頁2條,那很容易寫出sql為:select * from user order by create_time limit pageno,2;

在執行查詢過程中會發現:

1、查詢第一頁資料時:

第一頁查詢結果

2、查詢第四頁資料時:

第四頁查詢結果

user表共有8條資料,有4頁資料,但是實際查詢過程中第一頁與第四頁竟然出現了相同的資料。

這是什麼情況?難道上面的分頁sql不是先將兩個表關聯查詢出來,然後再排好序,再取對應分頁的資料嗎???

上面的實際執行結果已經證明現實與想像往往是有差距的,實際sql執行時並不是按照上述方式執行的。這裡其實是mysql會對limit做優化,具體優化方式見官方文件:

這個是5.7版本的說明,提取幾個問題直接相關的點做下說明。

上面官方文件裡面有提到如果你將limit row_count與order by混用,mysql會找到排序的row_count行後立馬返回,而不是排序整個查詢結果再返回。如果是通過索引排序,會非常快;如果是檔案排序,所有匹配查詢的行(不帶limit的)都會被選中,被選中的大多數或者全部會被排序,直到limit要求的row_count被找到了。如果limit要求的row_count行一旦被找到,mysql就不會排序結果集中剩餘的行了。

這裡我們檢視下對應sql的執行計畫:

可以確認是用的檔案排序,表確實也沒有加額外的索引。所以我們可以確定這個sql執行時是會找到limit要求的行後立馬返回查詢結果的。

不過就算它立馬返回,為什麼分頁會不准呢?

官方文件裡面做了如下說明:

paste_image.png

如果order by的字段有多個行都有相同的值,mysql是會隨機的順序返回查詢結果的,具體依賴對應的執行計畫。也就是說如果排序的列是無序的,那麼排序的結果行的順序也是不確定的。

基於這個我們就基本知道為什麼分頁會不准了,因為我們排序的字段是create_time,正好又有幾個相同的值的行,在實際執行時返回結果對應的行的順序是不確定的。對應上面的情況,第一頁返回的name為8的資料行,可能正好排在前面,而第四頁查詢時name為8的資料行正好排在後面,所以第四頁又出現了。

那這種情況應該怎麼解決呢?

官方給出了解決方案:

paste_image.png

如果想在limit存在或不存在的情況下,都保證排序結果相同,可以額外加乙個排序條件。例如id欄位是唯一的,可以考慮在排序欄位中額外加個id排序去確保順序穩定。

所以上面的情況下可以在sql再新增個排序字段,比如fund_flow的id欄位,這樣分頁的問題就解決了。修改後的sql可以像下面這樣:

select * fromuserorder by create_time,id limit 6,2;

再次測試問題解決!!

Mysql order by與limit混用陷阱

mysql order by與limit混用陷阱 在mysql中我們常常用order by來進行排序,使用limit來進行分頁,當需要先排序後分頁時我們往往使用類似的寫法select from 表名 order by 排序字段 limt m,n。但是這種寫法卻隱藏著較深的使用陷阱。在排序欄位有資料重...

mysql order by 優化與索引的應用

在某些場景,在不做額外的排序情況下,mysql 可以使用索引來滿足 order by 子句的優化。雖然 order by並不完全精確地匹配索引,但是索引還是會被使用,只要在where子句中,所有未被使用的那部分索引 乙個索引多個欄位的情況 以及所有order by欄位都是乙個常量就沒問題。下面這些查...

MySQL Order by 語句用法與優化詳解

order by語句是用來排序的,經常我們會使用到order by來進行排序,下面我給大家來講講order by用法與優化排序,有需要的同學可參考 複製 如下 select column name s from table name order by column name 例子 sql建立 複製 ...