order by和limit結合使用的問題

2021-10-05 13:34:53 字數 1477 閱讀 4362

在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要求的行後立馬返回查詢結果的。

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

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

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

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

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

官方給出了解決方案:

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

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

select * from user order by create_time,id limit 6,2;

SQL同時用orderby和limit查詢的坑

今日推薦 為什麼一到面試就懵逼!create table newtable id int 11 not null auto increment comment 部門id partnerid int 11 not null comment 線下合作商id department name varchar...

Limit結合使用SQL calc found

在很多分頁的程式中都這樣寫 select count from table where 查出符合條件的記錄總數 select from table where limit m,n 查詢當頁要顯示的資料 這樣的語句可以改成 select sql calc found rows from table w...

delete與limit的結合

在日常的開發工作中,或多或少都會遇到刪除單條資料的時候,那平時是怎麼寫的呢,是不是這樣 delete from table name where id 1 在刪除操作執行的過程中,如果命中了第一條資料,執行完刪除操作後還會去掃瞄全表,但是如果加上了limit 1 執行完就return了,效率高低,一...