2016-11-11 77 views
1

我有一個REST服務,它根據當前頁面和每頁結果返回數據庫表中的行。mySQL - 過濾行的分頁

當未過濾的結果,這是很容易做到的,我只是做一個SELECT WHERE ID> =(頁 - 1)* perPage + 1和LIMIT到perPage。

問題是當試圖對過濾結果使用分頁時,如果我選擇僅篩選WHERE type = someType的行。

在這種情況下,第一頁的第一個匹配可以在id 7開始,最後一個可以在id 5046中。然後第二個頁面的第一個匹配可以從7302開始並在12430結束,以此類推。

對於過濾結果的第一頁,我可以簡單地從id 1開始,LIMIT到perPage,但對於第二頁等,我需要知道上一個匹配行的索引頁面,甚至更好 - 當前頁面中第一個匹配的行或其他指示。

我該如何有效地做到這一點?我需要能夠在具有數百萬行的表上執行此操作,因此顯然讀取所有行並從中取出並不是一個選項。

的想法是這樣的:

SELECT ... FROM ... WHERE filterKey = filterValue AND id >= id_of_first_match_in_current_page

與id_of_first_match_in_current_page是其中的奧祕。

+1

這似乎是一個非常奇怪的方式來做到這一點。在我的經驗研究小型/中型數據集標準是爲了通過行,這樣就得到一個一致的數據集(這樣你就可以使用任何WHERE),然後使用LIMIT和,位我懷疑你可能已經錯過了,偏移子句告訴MySQL從位置X返回行。 –

+0

過濾的數據集通常可能有多大? – Strawberry

+0

喬恩 - 我明白了,我不熟悉OFFSET,謝謝。草莓 - 我希望能夠一次獲取200行的東西,從過濾的結果可以達到成千上萬。 –

回答

2

您無法知道給定頁面上的第一個ID是什麼,因爲ID號碼不一定是順序的。換句話說,序列中可能存在空白,所以100行的第五頁上的行不一定從id 500開始。它可以以id 527開始,例如,不可能知道。

換言之另一種方式:ID是一個值,而不是行號。

如果你的客戶是通過以升序頁推進一個可行的辦法是,每個REST請求獲取數據,指出值頁面上的最大 ID,然後使用該在下一個 REST請求,以便它查詢id值較大。

SELECT ... FROM ... WHERE filterKey = filterValue 
AND id > id_of_last_match_of_previous_page 

但是,如果您的REST請求可以獲取任意隨機頁面,則此解決方案不起作用。這取決於已經獲取了先前的頁面。

另一種解決方案是使用LIMIT <x> OFFSET <y>語法。這使您可以請求任何任意頁面。 LIMIT <y>, <x>的工作原理是相同的,但出於某種原因,x和y在兩種不同的語法形式中是相反的,所以請牢記這一點。

使用LIMIT...OFFSET在請求結果中包含多頁的頁面時效率不高。假設您要求第5,000頁。 MySQL必須在5,000頁的服務器端生成一個結果,然後丟棄其中的4,999個,並返回結果中的最後一頁。對不起,但這是它的工作原理。


回覆您的評論:

你必須明白,WHERE適用於行的條件,但網頁是由行位置定義。這是確定行的兩種不同方式!

如果您有一列保證爲行號,那麼您可以像使用行位置那樣使用該值。您甚至可以在其上放置索引,或將其用作主鍵。

但是,主鍵值可能會更改,並且可能不連續,例如,如果更新或刪除行或回滾某些事務等等。對主鍵值重新編號是一個壞主意,因爲其他表或外部數據可能會引用主鍵值。

所以你可以添加另一列而不是的主鍵,但只有一個行號。

ALTER TABLE MyTable ADD COLUMN row_number BIGINT UNSIGNED, ADD KEY (row_number); 

然後在需要對行重新編號時填寫值。

SET @row := 0; 
UPDATE MyTable SET row_number = (@row := @row + 1) ORDER BY id; 

例如,如果您曾刪除某些行,您將不得不重新編號行。經常這樣做效率不高,取決於表格的大小。

另外,如果不鎖定表格,新插入無法創建正確的行號值。這對於防止競爭條件是必要的。

如果您保證row_number是一系列連續值,那麼它既是一個值又是一個行位置,因此您可以將它用於任何任意行的頁面的高性能索引查找。

SELECT * FROM MyTable WHERE row_number BETWEEN 401 AND 500; 

至少直到下一次通過刪除或新插入對行號序列置疑。

+0

感謝您的詳細解答。有沒有一種有效的方法可以讓我請求任意的頁面?現在,隨着大數據的所有進步,人們必須想到一些事情。 –

+0

重新編輯 - 很好的答案。非常感謝你。 –

1

您正在使用ID列作錯誤的用途。 ID是記錄的標識符,而不是任何給定結果集的記錄的序列號

LIMIT關鍵字延伸到基本分頁。如果你只是想前10條記錄,你會做這樣的事情:

LIMIT 10 

進行分頁,如果你想要的 10條記錄,你會怎麼做:

LIMIT 10,10 

的10之後:

LIMIT 20,10 

依此類推。

LIMIT的子句是獨立於WHERE子句。使用WHERE來篩選結果,使用LIMIT分頁。