2017-02-16 70 views
2

我有一個People(Id, first_name, last_name),其中主鍵是id。我希望能夠查到(last_name, first_name, Id)訂購的表格中的前N個人。在某些情況下,我需要查找下N個人,等等。我想有效地做到這一點。做這個的最好方式是什麼?如何實現分頁?

回答

3

主要有兩種方式:

  • 使用LIMITOFFSET
  • 使用LIMIT和關鍵的,以前的頁面

的OFFSET策略讓您閱讀任意網頁,但因爲每次查詢運行時它都是無效的,所以它必須讀取所有先前頁面中的行。這是最容易實現的,並且可以成爲可接受的策略(特別是如果您只需要前幾頁),但通常不推薦。前一頁的關鍵策略確實需要按順序讀取頁面,但效率更高,因爲每個頁面只讀取所需的行。

因此,讓我們先從原來的查詢來獲取從表中的結果通過(LastName, FirstName, Id)下令:

SELECT 
    t.id, 
    t.first_name, 
    t.last_name 
FROM 
    People as t 
ORDER BY 
    t.last_name, 
    t.first_name, 
    t.id 
LIMIT 
    @limit_rows 

你可能會想,以確保您的查詢的所有查看數據庫數據的一致性快照,所以你」我們希望確保你的查詢序列總是從相同的時間戳中讀取。完成此操作的最簡單方法是將您的第一個查詢設置爲returnReadTimestamp設置爲true的ReadOnly事務。然後,您的後續查詢也可以是ReadOnly事務,並且它們應該使用由原始查詢返回的相同時間戳作爲它們的readTimestamp。請注意,無論您選擇哪種方法,ORDER BY條款對於確保您的查詢序列中的一致結果至關重要。 假設返回的最後一行是(1709, "John", "Smith")。然後你在查詢第一次嘗試,得到的結果的下一頁可能是這樣的:

SELECT 
    t.id, 
    t.first_name, 
    t.last_name 
FROM 
    People as t 
WHERE 
    t.last_name > "Smith" 
    OR 
    (t.last_name = "Smith" and t.first_name > "John") 
    OR 
    (t.last_name = "Smith" and t.first_name = "John" AND t.id > 1709) 
ORDER BY 
    t.last_name, 
    t.first_name, 
    t.id 
LIMIT 
    @limit_rows 

中間WHERE條款是新的。但是編寫這個謂詞比你想象的要複雜。您可能需要處理NULL值。您必須處理有多個名爲John Smith的人使用不同的id值的情況。而且您需要非常小心浮點數和NaN值。 Cloud Spanner的Read API在這種情況下也很有用,因爲它可以更容易地對錶格上的範圍掃描進行分頁。

+0

嗨邁克,感謝張貼這個。如果您使用快照並提供時間戳,爲什麼需要添加所有約束條件。你不能說t.id> 1709嗎?或者你是否試圖掩蓋快照因垃圾收集而過期的情況? – Bradford

+0

垃圾收集在這裏是無關緊要的。額外約束的原因是因爲問題表示我們希望按照(姓氏,名字,ID)的順序返回查詢結果,這與主鍵順序不同。請注意,可能有一個結果,其t.id小於1709,這仍然是一個需要的結果(例如「Wilkes」,「Bob」,805) –

0

MySQL和PostgreSQL的支持非常酷的功能,稱爲偏移通常用LIMIT子句中使用。

LIMIT子句用於限制SQL語句中返回結果的數量。所以,如果你有一臺1000行,但只想要回第10位,你會做這樣的事情:

SELECT column FROM table LIMIT 10 

這是類似於Microsoft SQL Server上的TOP子句。然而LIMIT子句總是在MySQL和PostgreSQL的查詢結尾。

現在假設你想顯示結果11-20。隨着關鍵字抵消其一樣簡單,下面的查詢將做到:

SELECT column FROM table LIMIT 10 OFFSET 10 

這可以很容易地編寫多頁結果或分頁與SQL。通常使用的方法是選擇所有記錄,然後在應用程序服務器層上進行篩選,而不是直接在數據庫上進行篩選。就像你會想象在數據庫上這樣做會產生更好的性能。