分頁
Prisma Client 支援偏移分頁和基於游標的分頁。
偏移分頁
偏移分頁使用 skip
和 take
來跳過一定數量的結果並選取有限的範圍。以下查詢跳過前 3 筆 Post
記錄,並傳回記錄 4 - 7
const results = await prisma.post.findMany({
skip: 3,
take: 4,
})
要實作結果頁面,您只需 skip
頁面數量乘以每頁顯示的結果數量即可。
✔ 偏移分頁的優點
- 您可以立即跳到任何頁面。例如,您可以
skip
200 筆記錄並take
10 筆,這會模擬直接跳到結果集的第 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 個結果(或第 1 頁)的 ID。下一個查詢的游標為 29
第二個查詢傳回在提供的游標之後(換句話說 - ID 大於 29)包含單字 "Prisma"
的前 4 筆 Post
記錄
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 筆 id
小於 200 的 Post
記錄,不包含游標
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',
},
})