【问题标题】:pagination use seek method (keyset)分页使用 seek 方法(keyset)
【发布时间】:2017-05-15 05:57:12
【问题描述】:

我对如何使用 seek 方法进行分页有疑问,
在我的查询中,我需要
1.对多列排序
2.某些列不是唯一的

例如,如果数据如下所示并查询select * from ... order by name asc, status asc ....(other column), id asc并获取8 1 3 10 2 5 6 7 9 4
现在,如果我想分页每次选择 2 行, 第一页与上述查询相同,只需添加限制 2 select * from ... order by name asc, status asc ....(other column), id asc
如何选择下一页?

如果只是 id 列,我知道它是检查顺序,如果 asc 则发现 id 大于先前结果中的最大 id。
但是如果列是多个并且有些不是唯一值,如何解决?

id | name | status | ...
1 b 0
2 b 0
3 b 0
4 j 0
5 f 0
6 g 0
7 h 0
8 a 0
9 i 0
10 b 0

问 1。

// example table
CREATE TABLE IF NOT EXISTS "table"(
    "id" SERIAL NOT NULL,
    "create_date" timestamp without time zone NOT NULL,
    "create_by_user_id" integer NOT NULL,
    "last_modified_date" timestamp without time zone DEFAULT NULL,
    "last_modified_by_user_id" integer DEFAULT NULL,
    "url_id" varchar(50) NOT NULL,
    "status" integer NOT NULL,
    "rank" integer NOT NULL,
    "name" varchar DEFAULT NULL,
    "name_slug" varchar DEFAULT NULL,
    "description" varchar DEFAULT NULL,
    "start_date" date NOT NULL,
    "end_date" date NOT NULL,
    PRIMARY KEY ("id"),
    UNIQUE ("url_id"),
    FOREIGN KEY ("create_by_user_id") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE
  );

// select first page
SELECT * FROM table 
  ORDER BY name ASC NULLS LAST
   , status ASC NULLS LAST
   , rank DESC NULLS LAST
   , id ASC
  LIMIT 2;

// select next page  How do I know use <, >, <=, >= in below ?
SELECT * FROM table
  WHERE name ?? operator prev_end_name
    AND  status ??  prev_end_status
    AND  rank ??  prev_end_rank
    AND  id ?? prev_end_id
  ORDER BY name ASC NULLS LAST
   , status ASC NULLS LAST
   , rank DESC NULLS LAST
   , id ASC
  LIMIT 2;

我做了一些研究,我在网上找到的所有相关帖子都显示相同的东西使用以前的 id ...但在现实生活中如何使用不同的和多列排序?像 instagram api 也使用以前的 id 但如何按许多其他列进行排序和排序..
http://use-the-index-luke.com/sql/partial-results/fetch-next-page
http://leopard.in.ua/2014/10/11/postgresql-paginattion
https://blog.jooq.org/2013/10/26/faster-sql-paging-with-jooq-using-the-seek-method/
https://www.citusdata.com/blog/2016/03/30/five-ways-to-paginate/
https://www.reddit.com/r/programming/comments/2crwyg/we_need_tool_support_for_keyset_pagination/

请不要提供使用页码设置偏移限制来分页的方式,这不是我要找的。

问题 2.
我正在使用 node.js,
不使用任何查询生成器 orm 库,
我的模型的一部分构建如下查询,
因此,如果需要对多列进行排序,则很难添加 seek 方法。
想知道人们通常如何在现实生活中的大站点 nodejs + postgres 应用程序中做到这一点, 任何建议将不胜感激!

if (typeof request.data.query.pagination_type != 'undefined') {
  if (request.data.query.pagination_type == 'seek') {
    if (typeof request.data.query.sort_order_list != 'undefined') {
      for (var i = 0; i < request.data.query.sort_order_list.length; i++) {
        if (request.data.query.sort_order_list[i].sort == 'name') {
          if (typeof request.data.query.prev_end_name != 'undefined') {
            if (!start) { dbQuery += ' AND'; } else { dbQuery += ' WHERE'; }
            if (start) { start = false; }
            if (request.data.query.sort_order_list[i].order == 'asc') {
              dbQuery += ` ee.name < $` + paramsIndex;
            } else if (request.data.query.sort_order_list[i].order == 'desc') {
              dbQuery += ` ee.name > $` + paramsIndex;
            }
            paramsIndex++;
            dbParams.push(request.data.query.prev_end_name);
          }
        } else if (request.data.query.sort_order_list[i].sort == 'start_date') {
...

查询输出类似

SELECT t.* 
  FROM task t WHERE 
  t.status = $1 
  AND t.rank >= $2 
  AND t.end_date >= $3 
  AND t.rank < $4 
  AND t.end_date < $5 
  AND t.name < $6 
  AND t.start_date > $7 
  ORDER BY t.rank asc NULLS LAST, 
    t.end_date asc NULLS LAST, 
    t.name asc NULLS LAST, 
    t.start_date desc NULLS LAST LIMIT $8

【问题讨论】:

  • 常规的OFFSET + LIMIT 方法有什么问题?这是通常的做法。
  • 没有错,我现在用offset + limit,提供两个参数输入,page或者previous id(如果输入previous id使用row_number查找offset)。我只是想尝试学习seek方法(keyset)

标签: sql node.js database postgresql pagination


【解决方案1】:

Seek 方法需要一个唯一的列进行排序。如果你想按状态升序排序,你需要做的是这样的:

SELECT t.* 
  FROM task t WHERE 
  t.status = $1 
  AND t.rank >= $2 
  AND t.end_date >= $3 
  AND t.rank < $4 
  AND t.end_date < $5 
  AND t.name < $6 
  AND t.start_date > $7 
  ORDER BY t.status asc NULLS LAST, 
    t.id asc NULLS LAST
LIMIT $8

这将为您提供按状态排序的页面,如果状态相同,则按 ID 排序。您可以使用 N 列执行此操作,请始终记住将 ID 放在最后。

为了检索下一页,您需要最后一项的状态/ID,查询将类似于:

SELECT t.* 
  FROM task t WHERE 
  t.status = $1 
  AND t.rank >= $2 
  AND t.end_date >= $3 
  AND t.rank < $4 
  AND t.end_date < $5 
  AND t.name < $6 
  AND t.start_date > $7

--Pagination Where Clause
  AND t.status > $9
  OR (t.status = $9 AND t.id > $10)
 
  ORDER BY t.status asc NULLS LAST, 
    t.id asc NULLS LAST
LIMIT $8

其中 $9 是最后一个项目的状态,$10 是最后一个项目的 ID(来自上一页)。 如果要按 DESC 排序,则分页 where 子句中的 &gt; 更改为 &lt;

【讨论】:

    【解决方案2】:

    假设您的 Id 是一个完整的数字集(没有删除的行),您可以按 Id 对子查询进行排序,将 WHERE 子句限制为页码(0 表示第一页返回 0 到 10,1 返回 11 到 20 等) .

        SELECT TOP 10 * FROM
            ( SELECT  *
            FROM Table
            WHERE Id > 5 * @PageNumber
            ORDER BY Id )
    

    【讨论】:

    猜你喜欢
    • 2018-10-25
    • 1970-01-01
    • 1970-01-01
    • 2010-12-25
    • 2019-05-01
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多