0
  • I have 2 tables

resources

enter image description here

resource_votes_aggregate which stores the number of likes per resource_id

enter image description here

  • I want to paginate through resources in descending order of their title using seek/keyset pagination

  • Without Pagination, this is what you get

enter image description here

  • I want the results in batches of 5 as I am paginating 5 results at a time

My PAGE 1 query worked without issues using the query below

SELECT
   r.resource_id,
   title,
   COALESCE(likes, 0) AS likes 
FROM
   resources r 
   LEFT JOIN
      resource_votes_aggregate a 
      ON r.resource_id = a.resource_id 
WHERE
   category_id = 1 
ORDER BY
   title DESC,
   resource_id DESC LIMIT 5;
  • How do I get the next page using keyset/seek pagination?

My attempt

SELECT
   r.resource_id,
   title,
   COALESCE(likes, 0) AS likes 
FROM
   resources r 
   LEFT JOIN
      resource_votes_aggregate a 
      ON r.resource_id = a.resource_id 
WHERE
   category_id = 1 
   AND 
   (
      likes,
      title
   )
   < (586, 'Zatoichi Meets Yojimbo (Zatôichi to Yôjinbô) (Zatôichi 20)') 
ORDER BY
   title DESC,
   resource_id DESC LIMIT 5;

Incorrect Results

enter image description here

UPDATE 1

I have created a FIDDLE HERE which loads 20 results? How to paginate this in batches of 5 using seek pagination?

Martin Smith
  • 438,706
  • 87
  • 741
  • 845
PirateApp
  • 5,433
  • 4
  • 57
  • 90

1 Answers1

1

There are two options that would do what you need:

OFFSET n FETCH FIRST n ROWS ONLY and sort_column > last_val

Example 1:

SELECT
   r.resource_id,
   title,
   COALESCE(likes, 0) AS likes 
FROM
   resources r 
   LEFT JOIN
      resource_votes_aggregate a 
      ON r.resource_id = a.resource_id 
WHERE
   category_id = 1 
   AND 
   (
      likes,
      title
   )
   < (586, 'Zatoichi Meets Yojimbo (Zatôichi to Yôjinbô) (Zatôichi 20)') 
ORDER BY
   title DESC,
   resource_id DESC
OFFSET 5
FETCH FIRST 5 ROWS ONLY;

You would then just change the OFFSET value each time.

Then the other way would be to keep track of the title and resource_id in your application layer, and then pass this to the query so that you start further down the result set:

SELECT
   r.resource_id,
   title,
   COALESCE(likes, 0) AS likes 
FROM
   resources r 
   LEFT JOIN
      resource_votes_aggregate a 
      ON r.resource_id = a.resource_id 
WHERE
   category_id = 1 
   AND 
   (
      likes,
      title
   )
   < (586, 'Zatoichi Meets Yojimbo (Zatôichi to Yôjinbô) (Zatôichi 20)')
   AND
     (title,resource_id) > (last_title_id_from_before,last_resource_id_from_before)
ORDER BY
   title DESC,
   resource_id DESC LIMIT 5;

This would ensure you skip rows from previous pages, but then downside with this strategy is you wouldn't have a way of going back a page again.

Thom Brown
  • 1,869
  • 4
  • 11
  • your solution does not work, i created a fiddle https://www.db-fiddle.com/f/dNnrjBRei15oLUFBd6y1mV/0 to test it – PirateApp May 22 '20 at 10:43
  • The query you used there doesn't match the query you posted here, and doesn't use either solution I proposed. I have adjusted your example to include of the solutions, and it's working for me: https://www.db-fiddle.com/f/dNnrjBRei15oLUFBd6y1mV/3 – Thom Brown May 22 '20 at 10:50
  • Does this answer your question? – Thom Brown May 26 '20 at 17:26
  • no this uses limit offset, seek pagination without a sequential scan is what i am looking for – PirateApp May 27 '20 at 03:39