键集/搜索分页和按搜索词过滤

tew*_*mat 6 sql postgresql pagination

使用表 products(id, title, price, category_id)

如何按标题、价格、category_id、件数过滤产品并使用键集/搜索分页检索第二页?一个页面有 10 个项目

https://blog.jooq.org/2016/08/10/why-most-programmers-get-pagination-wrong/

使用偏移分页的 SQL 将是

SELECT * FROM products
WHERE title like '%search_term%' AND price > 100 AND price < 400 AND category_id=11
ORDER BY price DESC
LIMIT 10 OFFSET 10
Run Code Online (Sandbox Code Playgroud)

结果可以按这个顺序

(id=23, title='Some text', price=354, category_id=11)
(id=41, title='Big text', price=333, category_id=11)
(id=43, title='big big text', price=333, category_id=11)
(id=38, title='A text', price=288, category_id=11)
(id=11, title='text', price=200, category_id=11)
Run Code Online (Sandbox Code Playgroud)

Luk*_*der 10

用于键集分页的纯 SQL 解决方案

在谈论键集分页时,首先要了解的是没有“第二”页这样的东西。给定“当前页面”,只有“下一页”。在您的情况下,当前页面将是在以下位置结束的页面:

(id=11, title='text', price=200, category_id=11)
Run Code Online (Sandbox Code Playgroud)

因此,下一页将是具有(price, id) < (200, 11)(当前价格,id)的页面。如果此查询生成您的第一页

SELECT * 
FROM products
-- "Ordinary predicates"
WHERE title LIKE '%search_term%' 
AND price > 100 AND price < 400 
AND category_id = 11
ORDER BY price DESC, id DESC
LIMIT 10
Run Code Online (Sandbox Code Playgroud)

然后,此查询将生成您的下一页

SELECT * 
FROM products
-- "Ordinary predicates"
WHERE title LIKE '%search_term%' 
AND price > 100 AND price < 400 
AND category_id = 11
-- "Keyset pagination predicate"
AND (price, id) < (200, 11)
ORDER BY price DESC, id DESC
LIMIT 10
Run Code Online (Sandbox Code Playgroud)

或者,该谓词可以扩展为:

-- "Keyset pagination predicates"
AND (price < 200 OR price = 200 AND id < 11)
Run Code Online (Sandbox Code Playgroud)

甚至到这个:

-- "Keyset pagination predicate"
AND price <= 200
AND (price < 200 OR price = 200 AND id < 11)
Run Code Online (Sandbox Code Playgroud)

根据数据库的不同,三个不同的谓词可能表现不同

一个 jOOQ 解决方案

由于您引用的是 jOOQ 博客,因此您将如何使用 jOOQ 在第二页上编写查询:

DSL.using(configuration)
   .selectFrom(PRODUCTS)
   .where(PRODUCTS.TITLE.like("%search_term%")
   .and(PRODUCTS.PRICE.gt(100))
   .and(PRODUCTS.PRICE.lt(400))
   .and(PRODUCTS.CATEGORY_ID.eq(11))
   .orderBy(PRODUCTS.PRICE.desc(), PRODUCTS.ID.desc())
   .seek(200, 11) // Automatic generation of keyset pagination predicates
   .limit(10)
   .fetch();
Run Code Online (Sandbox Code Playgroud)