Share via


分頁

分頁是指在頁面中擷取結果,而不是一次擷取結果;這通常是針對大型結果集完成的,其中會顯示使用者介面,讓使用者流覽至結果的下一頁或上一頁。

警告

不論使用的分頁方法為何,請務必確定您的訂購完全是唯一的。 例如,如果結果只依日期排序,但同一日期可能會有多個結果,則當分頁時,結果在分頁時可能會略過,因為它們在兩個分頁查詢之間以不同方式排序。 依日期和識別碼排序(或任何其他唯一屬性或屬性組合)的排序會讓排序完全是唯一的,並避免此問題。 請注意,關係資料庫預設不會套用任何排序,即使是在主鍵上也一樣。

位移分頁

使用資料庫實作分頁的常見方式是使用 SkipTakeOFFSETLIMIT 在 SQL 中)。 假設頁面大小為 10 個結果,可以使用 EF Core 擷取第三頁,如下所示:

var position = 20;
var nextPage = context.Posts
    .OrderBy(b => b.PostId)
    .Skip(position)
    .Take(10)
    .ToList();

不幸的是,雖然這項技術非常直覺,但它也有一些嚴重的缺點:

  1. 資料庫仍必須處理前 20 個專案,即使它們未傳回至應用程式也一樣;這會建立可能會大幅計算負載,並隨著略過的資料列數目而增加。
  2. 如果同時發生任何更新,您的分頁最終可能會略過特定專案或顯示兩次。 例如,如果使用者從第 2 頁移至 3 時移除專案,則整個結果集會「向上移位」,而且會略過一個專案。

索引鍵集分頁

以位移為基礎的分頁的建議替代方案,有時稱為 索引鍵集分頁 搜尋型分頁 -- 只是使用 WHERE 子句來略過資料列,而不是位移。 這表示請記住最後一個專案擷取的相關值(而不是其位移),並要求在該資料列之後的下一個資料列。 例如,假設我們擷取的最後一個頁面最後一個專案的識別碼值為 55,我們只需要執行下列動作:

var lastId = 55;
var nextPage = context.Posts
    .OrderBy(b => b.PostId)
    .Where(b => b.PostId > lastId)
    .Take(10)
    .ToList();

假設索引是在 上 PostId 定義,此查詢會非常有效率,而且不會區分任何在較低識別碼值中發生的並行變更。

索引鍵集分頁適用于使用者向前和向後巡覽的分頁介面,但不支援隨機存取,使用者可以跳至任何特定頁面。 隨機存取分頁需要使用位移分頁,如上所述;由於位移分頁的缺點,請仔細考慮您的使用案例是否真的需要隨機存取分頁,或下一頁/上一頁導覽是否足夠。 如果需要隨機存取分頁,強固的實作可以在流覽至下一頁/上一頁時使用索引鍵集分頁,並在跳至任何其他頁面時位移流覽。

多個分頁索引鍵

使用索引鍵集分頁時,經常需要依多個屬性排序。 例如,下列查詢會依日期和識別碼分頁:

var lastDate = new DateTime(2020, 1, 1);
var lastId = 55;
var nextPage = context.Posts
    .OrderBy(b => b.Date)
    .ThenBy(b => b.PostId)
    .Where(b => b.Date > lastDate || (b.Date == lastDate && b.PostId > lastId))
    .Take(10)
    .ToList();

這可確保下一頁會確切地挑選上一個頁面結束的位置。 隨著新增更多排序索引鍵,可以新增其他子句。

注意

大部分的 SQL 資料庫都支援更簡單且更有效率的上述版本,使用 資料列值 WHERE (Date, Id) > (@lastDate, @lastId) 。 EF Core 目前不支援在 LINQ 查詢中表示此內容,這會由 #26822 追蹤。

索引數

和任何其他查詢一樣,適當的索引編制對於良好的效能至關重要:請務必有對應至您分頁順序的索引。 如果依多個資料行排序,則可以定義這些多個資料行的索引;這稱為 複合式索引

如需詳細資訊, 請參閱索引 的檔頁面。

其他資源