分頁
Prisma Client 支援偏移分頁和基於遊標的分頁。
偏移分頁
偏移分頁使用 skip 和 take 來跳過一定數量的結果並選擇一個有限的範圍。以下查詢跳過前3條 Post 記錄並返回第4至第7條記錄
const results = await prisma.post.findMany({
skip: 3,
take: 4,
})

要實現結果分頁,只需 skip 頁面數乘以每頁顯示的結果數。
✔ 偏移分頁的優點
- 您可以立即跳轉到任何頁面。例如,您可以
skip200條記錄並take10條,這模擬了直接跳轉到結果集的第21頁(底層SQL使用OFFSET)。這在基於遊標的分頁中是不可能實現的。 - 您可以以任何排序順序對相同的結果集進行分頁。例如,您可以跳轉到按名字排序的
User記錄列表的第21頁。這在基於遊標的分頁中是不可能實現的,因為它要求按唯一、順序的列進行排序。
✘ 偏移分頁的缺點
- 偏移分頁在資料庫層面**無法擴充套件**。例如,如果您跳過200,000條記錄並取出前10條,資料庫仍然需要遍歷前200,000條記錄才能返回您請求的10條——這會負面影響效能。
偏移分頁的用例
- 對小型結果集進行淺層分頁。例如,一個允許您按作者過濾
Post記錄並對結果進行分頁的部落格介面。
示例:過濾和偏移分頁
以下查詢返回 email 欄位包含 prisma.io 的所有記錄。該查詢跳過前40條記錄並返回第41至第50條記錄。
const results = await prisma.post.findMany({
skip: 40,
take: 10,
where: {
email: {
contains: 'prisma.io',
},
},
})
示例:排序和偏移分頁
以下查詢返回 email 欄位包含 Prisma 的所有記錄,並按 title 欄位排序結果。該查詢跳過前200條記錄並返回第201至第220條記錄。
const results = await prisma.post.findMany({
skip: 200,
take: 20,
where: {
email: {
contains: 'Prisma',
},
},
orderBy: {
title: 'desc',
},
})
基於遊標的分頁
基於遊標的分頁使用 cursor 和 take 來返回給定**遊標**之前或之後有限數量的結果。遊標會在結果集中標記您的位置,並且必須是一個唯一、順序的列——例如 ID 或時間戳。
以下示例返回前4條包含單詞 "Prisma" 的 Post 記錄,並將最後一條記錄的 ID 儲存為 myCursor
**注意**:由於這是第一次查詢,沒有遊標可傳入。
const firstQueryResults = await prisma.post.findMany({
take: 4,
where: {
title: {
contains: 'Prisma' /* Optional filter */,
},
},
orderBy: {
id: 'asc',
},
})
// Bookmark your location in the result set - in this
// case, the ID of the last post in the list of 4.
const lastPostInResults = firstQueryResults[3] // Remember: zero-based index! :)
const myCursor = lastPostInResults.id // Example: 29
下圖顯示了前4個結果的 ID——即第1頁。下一次查詢的遊標是 **29**

第二次查詢返回包含單詞 "Prisma" 並在**提供的遊標之後**的前4條 Post 記錄(換句話說——ID 大於 **29** 的記錄)
const secondQueryResults = await prisma.post.findMany({
take: 4,
skip: 1, // Skip the cursor
cursor: {
id: myCursor,
},
where: {
title: {
contains: 'Prisma' /* Optional filter */,
},
},
orderBy: {
id: 'asc',
},
})
const lastPostInResults = secondQueryResults[3] // Remember: zero-based index! :)
const myCursor = lastPostInResults.id // Example: 52
下圖顯示了 ID 為 **29** 的記錄**之後**的前4條 Post 記錄。在此示例中,新遊標是 **52**

常見問題
我總是需要 skip: 1 嗎?
如果您不 skip: 1,您的結果集將包含您之前的遊標。第一次查詢返回四個結果,遊標是 **29**

不使用 skip: 1,第二次查詢返回遊標之後(並*包含*)的4個結果

如果您 skip: 1,則不包含遊標

您可以根據所需的分頁行為選擇是否使用 skip: 1。
我可以猜測遊標的值嗎?
如果您猜測下一個遊標的值,您將在結果集中分頁到未知位置。儘管 ID 是順序的,但您無法預測增量速率(2、20、32 比 1、2、3 更常見,尤其是在過濾後的結果集中)。
基於遊標的分頁是否使用底層資料庫中游標的概念?
不,遊標分頁不使用底層資料庫中的遊標(例如 PostgreSQL)。
如果遊標值不存在會發生什麼?
使用不存在的遊標會返回 null。Prisma Client 不會嘗試定位相鄰值。
✔ 基於遊標的分頁的優點
- 基於遊標的分頁**可擴充套件**。底層 SQL 不使用
OFFSET,而是查詢所有 ID 大於cursor值的Post記錄。
✘ 基於遊標的分頁的缺點
- 您必須按遊標進行排序,遊標必須是唯一、順序的列。
- 您無法僅使用遊標跳轉到特定頁面。例如,如果不首先請求第1至第399頁,您無法準確預測哪個遊標代表第400頁(每頁20條)的開頭。
基於遊標的分頁的用例
- 無限滾動——例如,按日期/時間降序排序部落格文章,並一次請求10篇部落格文章。
- 分批遍歷整個結果集——例如,作為長時間執行的資料匯出的一部分。
示例:過濾和基於遊標的分頁
const secondQuery = await prisma.post.findMany({
take: 4,
cursor: {
id: myCursor,
},
where: {
title: {
contains: 'Prisma' /* Optional filter */,
},
},
orderBy: {
id: 'asc',
},
})
排序和基於遊標的分頁
基於遊標的分頁要求您按順序、唯一的列進行排序,例如 ID 或時間戳。此值(稱為遊標)會在結果集中標記您的位置,並允許您請求下一組資料。
示例:使用基於遊標的分頁向後翻頁
要向後翻頁,請將 take 設定為負值。以下查詢返回4條 Post 記錄,其 id 小於200,不包括遊標
const myOldCursor = 200
const firstQueryResults = await prisma.post.findMany({
take: -4,
skip: 1,
cursor: {
id: myOldCursor,
},
where: {
title: {
contains: 'Prisma' /* Optional filter */,
},
},
orderBy: {
id: 'asc',
},
})