Mysql order by與limit混用陷阱

2021-10-05 23:06:27 字數 1645 閱讀 3346

mysql order by與limit混用陷阱

在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版本的說明,提取幾個問題直接相關的點做下說明。

paste_image.png

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

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

paste_image.png

可以確認是用的檔案排序,表確實也沒有加額外的索引。所以我們可以確定這個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 * from user order by create_time,id limit 6,2;

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建立 複製 ...