Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How can I achieve the same effect as ROW_NUMBER when using pagination?

I want to add count of rows per group of one of the columns. It works fine with regular queries, but not with pagination because each page starts the count from 0.

For example, I have the following table:

id | category_id | item_id | created_at
---|-------------|---------|-----------
 7 |      11     |   106   | 2024-05-06
 6 |      3      |   102   | 2024-05-06
 5 |      11     |   101   | 2024-05-05
 4 |      9      |   98    | 2024-05-04
 3 |      3      |   97    | 2024-05-03
 2 |      1      |   91    | 2024-05-02
 1 |      11     |   89    | 2024-05-01

And I want to count what order the item_id is in its category by the creation date.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

For that I’m using the following query:

DB::('table')->select(
    table.*,
    DB::raw('ROW_NUMBER() OVER(PARTITION BY category_id ORDER BY created_at) as order')
)->get();

Which is the same as the following raw query:

SELECT table.*, ROW_NUMBER() OVER(PARTITION BY category_id ORDER BY created_at) as order 
FROM table

And it results in:

id | category_id | item_id | created_at | order
---|-------------|---------|------------|-------
 7 |      11     |   106   | 2024-05-06 |    3
 6 |      3      |   102   | 2024-05-06 |    2
 5 |      11     |   101   | 2024-05-05 |    2
 4 |      9      |   98    | 2024-05-04 |    1
 3 |      3      |   97    | 2024-05-03 |    1
 2 |      1      |   91    | 2024-05-02 |    1  
 1 |      11     |   89    | 2024-05-01 |    1

But it won’t work with pagination because each page has its own subset and the count starts from another row, the if for example I paginate the above table every 4 results, and end up with 2 pages, then page number 2 would result in the following:

id | category_id | item_id | created_at | order
---|-------------|---------|------------|-------
 7 |      11     |   106   | 2024-05-06 |    2
 6 |      3      |   102   | 2024-05-06 |    1
 5 |      11     |   101   | 2024-05-05 |    1

But I want it to be

id | category_id | item_id | created_at | order
---|-------------|---------|------------|-------
 7 |      11     |   106   | 2024-05-06 |    3
 6 |      3      |   102   | 2024-05-06 |    2
 5 |      11     |   101   | 2024-05-05 |    2

How can I do it?

>Solution :

Since pagination using the ->paginate() method uses LIMIT and OFFSET in the background, I don’t think you can.

However, if you have no qualms about getting all the results and THEN returning only a subset of them with pagination, then you should be able to do it.

Caching the query’s results could help as well.

use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Cache;

// query
$ttl = 3600; // 60 * 60 seconds (1 hour)
$results = Cache::remember('query-results', $ttl, function () {
    return DB::('table')
        ->select(
            table.*,
            DB::raw('ROW_NUMBER() OVER(PARTITION BY category_id ORDER BY created_at) as order')
        )
        ->get();
});
// pagination
$page = request()->query('page', 1); // using the ?page= query parameter to get the current page
$perPage = 4;
$paginator = new LengthAwarePaginator(
    $results->forPage($page, $perPage),
    $results->count(),
    $perPage,
    $page,
);
  
// return response()->json(['data' => $paginator]);
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading