1. 程式人生 > >MySQL - 當LIMIT 進行分頁時,為什麼出現了重複資料

MySQL - 當LIMIT 進行分頁時,為什麼出現了重複資料

哦,這時寫的一個破SQL,遺留了個問題,沒有去注意,所以造成了,有重複資料。因為引用了 PageHelper 外掛,期初還以為是 外掛有問題。後來想想,畢竟整個框架都是用的這個外掛,就算有問題早應該會出現了。所以,第一時間想到了SQL,的確,去了排序就沒有問題。

說在前面

資料庫分頁是後臺經常要使用的技術手段,有時候進行資料庫查詢會根據業務需要對某一欄位排序,那麼當待排序欄位值相同時,我們得到的查詢結果會是什麼呢

問題描述

資料分頁時需要根據資料記錄建立時間create_time欄位倒序,即使用order by create_time desc,但是我們會發現,前端進行請求時獲取的資料並不正確,分頁中出現了一定的重複資料。

問題原因

期初還很好奇,總數沒問題,總查詢也沒問題,為什麼資料會重複了,然後會把部分資料給覆蓋了。後來,通過檢視SQL發現,是根據時間進行排序的,然而 這個時間 恰恰 好多資料都是 同一時間插入,或者 設定的 同一時間。

先後執行 總查詢(也就是不分頁),是沒有重複。

再次執行分頁查詢,分兩頁查詢就有了出路。(且,兩次查詢出來的資料和總查詢資料不一樣了)

後來 發現,當SQL中ORDER BY待排序欄位值相同時,系統對資料的排序可能變得隨機,即一會兒這條資料在前面,一會兒這條資料在後面了 ,所以當翻頁的時候我們很容易便看到了重複的資料。

當然 資料重複,你是不知道哪個是真的?哪個是假的?的。意思就是,第一頁你看到了123,第二頁看到了,345。或許真正的排序是 123645。

舉個例子

我們可以通過一組資料做個簡單實驗,以下為一組實驗資料,member_id欄位為資料主鍵,資料的create_time欄位完全相同

我們執行以下SQL,將資料以create_time欄位倒序查詢,查詢結果如下:

select member_id,create_time from member order by create_time desc;

查詢結果:

 我們發現查詢結果中,資料排序變成了一種無序狀態,這也是導致我們分頁查詢時出現重複資料的問題原因
 我們執行以下SQL,將資料以create_time欄位倒序後再根據主鍵排序查詢

,查詢結果如下:

select member_id,create_time from member order by create_time desc,member_id;

我們發現數據恢復了有序的狀態。這也為我們提供了避免資料分頁時待排序欄位值相同情況時結果無序的解決方案。

SQL中ORDER BY相同值結果亂序的具體原因

查閱了Goole和相關資料,大概總結了這種情況的原因。其實發生這種現象是“故意”設計的。

如果沒有指定ORDER BY語句,則SQL Server(或任何RDBMS)不保證以特定順序返回結果。 有些人認為,如果沒有指定order by子句,行總是以聚簇索引順序或物理磁碟順序返回。 然而,這是不正確的,因為在查詢處理期間可以改變行順序的許多因素,例如並行的HASH連線是更改行順序的操作符的一個很好的例子。

如果指定ORDER BY語句,SQL Server將對行進行排序,並按請求的順序返回。 但是,如果該順序不是確定性的,即可能有重複的值,則在每個具有相同值的組中,由於與上述相同的原因,該順序是“隨機的”

確保確定性順序的唯一方法是在ORDER BY子句中包含保證的唯一列或列組(例如主鍵)。

總結

為了避免類似的問題,我們可以將主鍵(或者具有唯一性的欄位)排序引入需要排序的業務欄位後。