【问题标题】:SQL to Paginate Data Where Pagination Starts at a Given Primary KeySQL 对从给定主键开始分页的数据进行分页
【发布时间】:2014-12-17 04:05:53
【问题描述】:

编辑: 我使用的原始示例有一个 int 作为主键,而实际上我的主键是一个包含 UUID 作为字符串的 var char。我已更新以下问题以反映这一点。

警告:解决方案必须适用于 postgres。

问题:从已知页码或索引到要分页的结果列表中时,我可以轻松地对数据进行分页,但如果我只知道该行的主键,如何做到这一点从开始。例如说我的表有这些数据

TABLE: article
======================================
id           categories    content
--------------------------------------
B7F79F47     local         a
6cb80450     local         b
563313df     local         c
9205AE5A     local         d
E88F7520     national      e
5ab669a5     local         f
fb047cf6     local         g
591c6b50     national      h
======================================

给定文章主键“9205AE5A”(article.id == '9205AE5A'),并且类别列必须包含“本地”,如果它是分页的,即返回的结果应该包含 3 个项目(上一篇、当前、下一篇)

('563313df','local','c'),('9205AE5A','local','d'),('5ab669a5','local','f')

这是我的示例设置:

-- setup test table and some dummy data
create table article (
  id varchar(36),
  categories varchar(256),
  content varchar(256) 
)
insert into article values
('B7F79F47', 'local', 'a'),
('6cb80450', 'local', 'b'),
('563313df', 'local', 'c'),
('9205AE5A', 'local', 'd'),
('E88F7520', 'national', 'e'),
('5ab669a5', 'local', 'f'),
('fb047cf6', 'local', 'g'),
('591c6b50', 'national', 'h');

我想对文章表中的行进行分页,但我的起点是文章的“id”。为了在呈现的页面上提供“上一篇文章”和“下一篇文章”链接,我还需要这篇文章两边的文章我知道

的 id

在服务器端,我可以运行分页 sql 并遍历每个结果集以查找给定项目的索引。请参阅以下低效伪代码/sql 来执行此操作:

page = 0;
resultsPerPage = 10;
articleIndex = 0;
do {
    resultSet = select * from article where categories like '%local%' limit resultsPerPage offset (page * resultsPerPage) order by content;
    for (result in resultSet) {
        if (result.id == '9205AE5A') {
           // we have found the articles index ('articleIndex') in the paginated list. 
           // Now we can do a normal pagination to return the list of 3 items starting at the article prior to the one found
           return select * from article where categories like '%local%' limit 3 offset (articleIndex - 1);
        }
        articleIndex++;
    }
    page++;
} while (resultSet.length > 0);

如果给定的文章在分页列表的下方,这将非常缓慢。如果没有丑陋的 while+for 循环,如何做到这一点?

编辑2:我可以使用两个sql调用得到结果

SELECT 'CurrentArticle' AS type,* FROM
  (
   SELECT (ROW_NUMBER() OVER (ORDER BY content ASC)) AS RowNum,* 
   FROM article 
   WHERE categories LIKE '%local%' 
   ORDER BY content ASC
  ) AS tagCloudArticles
WHERE id='9205AE5A' 
ORDER BY content ASC 
LIMIT 1 OFFSET 0

从该结果返回,例如

('CurrentArticle', 4, '9205AE5A', 'local', 'd')

我可以得到RowNum值(4)然后再次运行sql得到RowNum+1(5)和RowNum-1(3)

SELECT 'PrevNextArticle' AS type,* FROM
  (
   SELECT (ROW_NUMBER() OVER (ORDER BY content ASC)) AS RowNum,* 
   FROM article 
   WHERE categories LIKE '%local%' 
   ORDER BY content ASC
  ) AS tagCloudArticles
WHERE RowNum in (3, 5)
ORDER BY content ASC 
LIMIT 2 OFFSET 0

结果

   ('PrevNextArticle', 3, '563313df', 'local', 'c'),
   ('PrevNextArticle', 5, '5ab669a5', 'local', 'f')

如果能在一个高效的 sql 调用中执行此操作会很好。

【问题讨论】:

  • 那么订购标准是什么?内容?时间戳?一个集合是无序的。
  • 在上面的示例中,我使用了“内容”,但实际上可以假设它是我未显示的任意数量的其他列,例如发布的时间戳、标签、作者、标题等。
  • @ShaneRowatt 我已经发布了答案。这是正确的还是我理解错了你的问题。

标签: sql postgresql pagination


【解决方案1】:

如果页面中显示的周围文章的唯一信息是“Next”和“Previous”,则无需提前获取它们的行。当用户选择“上一个”或“下一个”时,使用这些查询SQL Fiddle

-- Previous
select *
from article
where categories = 'local' and id < 3
order by id desc
limit 1
;

-- Next
select *
from article
where categories = 'local' and id > 3
order by id
limit 1
;

如需获取上、下篇文章信息:SQL Fiddle

with ordered as (
    select
        id, content,
        row_number() over(order by content) as rn
    from article
    where categories = 'local'
), rn as (
    select rn
    from ordered
    where id = '9205AE5A'
)
select
    o.id,
    o.content,
    o.rn - rn.rn as rn
from ordered o cross join rn
where o.rn between rn.rn -1 and rn.rn + 1
order by o.rn

文章将具有rn -1、0、1(如果存在)。

【讨论】:

  • 在这种情况下不是。下一个链接显示了下一页的摘要,因此它需要分页列表中的下一篇文章。与上一个链接相同。我也过度简化了这个问题。主键字段“id”实际上是一个包含 UUID 的 var char(36)。
  • 对我有用。我永远不会想出那个。谢谢。
【解决方案2】:

检查以下查询是否解决了您的问题。过滤器中也传递了 id,类别为:

SELECT * FROM
(
select (1 + row_number() OVER(Order BY id ASC)) AS RowNo,* from article where categories like '%local%' and id>=3
UNION
(SELECT 1,* FROM article where categories like '%local%' and id<3 ORDER BY id DESC LIMIT 1)
) AS TEMP
WHERE
  RowNo between 1 and (1+10-1)
ORDER BY
  RowNo

【讨论】:

    【解决方案3】:

    我认为这个查询会给你结果

    (SELECT *, 2 AS ordering from article where categories like '%local%' AND id = 3 LIMIT 1)
    UNION
    (SELECT *, 1 AS ordering from article where categories like '%local%' AND id < 3 ORDER BY id DESC LIMIT 1 )
    UNION
    (SELECT *, 3 AS ordering from article where categories like '%local%' AND id > 3 ORDER BY id ASC LIMIT 1 )
    

    【讨论】:

    • 感谢 Nandakaumar V,但我改写了这个问题,因为 id 字段应该是一个没有隐含顺序的 var char 字段。
    • @ShaneRowatt 所以你已经通过content.. 订购了它吗?
    猜你喜欢
    • 2011-01-23
    • 1970-01-01
    • 1970-01-01
    • 2019-06-09
    • 2015-05-02
    • 2020-06-28
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    相关资源
    最近更新 更多