确定 Postgres 查询中目标记录的偏移量

问题描述 投票:0回答:1

问题:我正在 Postgres 之上构建一个 RESTful API,其中包含以下参数:

  • 表中特定记录的标识符
    <id>
  • 过滤和排序参数(可选)
  • count
    或页面大小(可选)

例如:

/presidents/16?filter=!isAlive&sort=lastName,givenNames&count=5

API 返回

count
(或可能更少)条记录,其中包括由
<id>
指定的记录以及返回的记录的
offset
count

在上面的示例中,结果可能如下所示:

{
  "count": 5,
  "offset": 20,
  "records": [
    { "id": 17, "givenNames": "Andrew",    "lastName": "Johnson", "isAlive": false },
    { "id": 36, "givenNames": "Lyndon B.", "lastName": "Johnson", "isAlive": false },
    { "id": 35, "givenNames": "John F.",   "lastName": "Kennedy", "isAlive": false },
    { "id": 16, "givenNames": "Abraham",   "lastName": "Lincoln", "isAlive": false },
    { "id": 4,  "givenNames": "James",     "lastName": "Madison", "isAlive": false }
  ]
}

当前解决方案:我当前的方法是串行进行三个调用(将它们组合成单个嵌套查询,但效率问题仍然存在),我正在寻找更好的方法。

  1. 获取目标

    <id>
    引用的记录,在查询#2中使用。

    • 例如
      select * from presidents where id = 16
  2. 获取目标的偏移量

    <id>

    • 例如
      select count(*) from presidents where lastName < 'Lincoln' and givenNames < 'Abraham' and not isAlive order by lastName, givenNames, id
  3. 计算适当的

    offset
    limit
    后,使用传递的(或默认)
    count
    和#2中的
    count(*)
    ,检索记录页面。

    • 例如
      select * from presidents where not isAlive order by lastName, givenNames, id offset 20 limit 5

更新了SQL

我采用了@ErwinBrandstetter 在下面的答案中提供的内容,这与我正在寻找的怪物声明的方式很接近,并将其更改为:

WITH prez AS (SELECT lastName, givenNames, id, isAlive FROM presidents WHERE not isAlive),
cte AS (SELECT * FROM prez WHERE id = 16),
start AS (SELECT (COUNT(*)/5)*5 as "offset" FROM prez WHERE (lastName, givenNames, id, isAlive) < (TABLE cte))
SELECT row_to_json(sub2) AS js
FROM  (
 SELECT (SELECT * FROM start) AS "offset"
       , count(*) AS "count"
       , array_agg(sub1) AS records
 FROM  (
    SELECT * from prez
    ORDER  BY lastName, givenNames, id
    OFFSET (SELECT * from start) LIMIT  5
    ) sub1
) sub2;

SQL 小提琴

Postgres 中是否有更简单的方法来确定给定查询的给定记录的偏移量?

相关问题:

sql json postgresql pagination aggregate-functions
1个回答
2
投票

您正在寻找的一站式商店:

WITH cte AS (SELECT lastName, givenNames, id AS x FROM presidents WHERE id = 16)
SELECT row_to_json(sub2) AS js
FROM  (
   SELECT (SELECT count(*) FROM presidents
           WHERE (lastName, givenNames, id) < (TABLE cte)) AS "offset"
         , count(*) AS "count"
         , array_agg(sub1) AS records
   FROM  (
      SELECT id, givenNames, lastName, isAlive
      FROM   presidents
      WHERE (lastName, givenNames, id) >= (TABLE cte)
      ORDER  BY lastName, givenNames, id
      LIMIT  5
      ) sub1
  ) sub2;

SQL 小提琴。

您的原始查询#2 不正确

SELECT * FROM presidents
WHERE lastName < 'Lincoln'
AND   givenNames < 'Abraham'
AND   id < 16 ...

要保留排序顺序,它必须是:

SELECT * FROM presidents
WHERE (lastName, givenNames, id) < ('Lincoln', 'Abraham', 16) ...

比较行类型,而不是对各个列进行 AND 表达式,这会产生完全不同的结果。这就是

ORDER BY
有效运作的方式。

除了 (lastName, givenNames, id) 上的 PRIMARY KEY 之外,您还应该在

id
上有一个
multicolumn 
index
,以使其快速

CTE 中带有
row_number()
的变体

根据您更新的要求。

WITH prez AS (
   SELECT lastName, givenNames, id, isAlive
        , row_number() OVER (ORDER BY lastName, givenNames, id) AS rn
   FROM   presidents
   WHERE  NOT isAlive
   )
, x AS (SELECT ((rn-1)/5)*5 AS "offset" FROM prez WHERE id = 16)
SELECT row_to_json(sub2) AS js
FROM  (
   SELECT (SELECT "offset" FROM x)
        , count(*) AS "count"
        , array_agg(sub1) AS records
   FROM  (
      SELECT lastName, givenNames, id, isAlive
      FROM   prez
      WHERE  rn > (SELECT "offset" FROM x)
      ORDER  BY rn
      LIMIT  5
      ) sub1
   ) sub2;

SQL 小提琴。

使用

EXPLAIN ANALYZE
测试性能。

© www.soinside.com 2019 - 2024. All rights reserved.