背景說明
今天寫出一個(gè)十分弱智的bug,記錄一下,提醒自己以后別這種犯錯(cuò),不怕丟人哈~
在寫一個(gè)分頁查詢記錄的sql時(shí),要根據(jù)添加的時(shí)間逆序分頁輸出,之前的寫法是醬紫
select
record.a,
y.c
from
(
select
a,b
from
x
order by timestamp desc
limit 0,10
) record
left join y
on record.b = y.d;
因?yàn)橐恍┬碌男枨?,要在后面加一些where條件,limit操作不能在嵌套查詢里面加了,于是乎把limit 0,10提出來放到最外面,結(jié)果order by還留在里面,我當(dāng)時(shí)想嵌套查詢出來的record表已經(jīng)按timestamp字段逆序排列了,再left另一張表,最終再limit出來的結(jié)果應(yīng)該也是逆序的,但結(jié)果卻很打臉,是正序的。
分析原因
- 首先控制變量,代碼回滾到之前,把后來加的各種邏輯都去掉,還原到上述sql,只把limit 0,10移到最后,發(fā)現(xiàn)timestamp是正序的,那么問題應(yīng)該就出在這里了,與后來加的其他邏輯沒有關(guān)系。
- 那么再試一下刪掉limit操作,結(jié)果timestamp是無序的!這不可能啊,于是認(rèn)真看了下數(shù)據(jù),發(fā)現(xiàn)一些規(guī)律,可能是按y表的自增id或created_at時(shí)間字段排序的(因?yàn)檫@兩個(gè)字段是索引字段),那么到這里,我們至少可以得到一個(gè)簡單的結(jié)論,就是聯(lián)表查詢結(jié)果,不是按照嵌套查詢中的order by排序的,現(xiàn)在正向一看,確實(shí)不可能按這個(gè)排序,因?yàn)槔ㄌ柪锩娴倪壿媽ㄌ柾馐遣豢梢姷摹?/li>
-
還有個(gè)問題,上述去掉limit后,最終不是按left join主表的順序輸出,按照我們常理想象,mysql是循環(huán)主表的記錄去關(guān)聯(lián)另一張表,那么輸出的順序應(yīng)該還是主表的順序啊,但結(jié)果卻是按另一張表的字段排序的,這又是為什么呢?
去官方手冊中找找線索,發(fā)現(xiàn)order by模塊中有這么一句話。
order by模塊
再去limit模塊中看一下
limit模塊
從以上兩個(gè)截圖中,我們可以發(fā)現(xiàn)一些端倪,limit操作會對查詢有一些優(yōu)化,查詢到指定條數(shù)的數(shù)據(jù),就可以提前結(jié)束了,比如我們本文中的left操作,拿到10條結(jié)果就結(jié)束查詢線程,返回客戶端。我猜測,如果沒有l(wèi)imit操作,反正全部都要join,可能mysql會對循環(huán)邏輯做一些優(yōu)化,不一定要按主表來循環(huán),思想類似于java編譯中的重排序,也對應(yīng)了上面截圖中的那句話。
解決方案
采用最簡單、最粗暴的方式,直接把order by 和 limit操作放到最外面就ok啦,其實(shí)效率上并沒有什么降低,只要索引建的合理即可。

