Skip to content

Improving pagination performance #870

@alexc-jeromes

Description

@alexc-jeromes

Pagination is insanely slow.

This code:

$items->paginate(25)

and produces:

select t2.* from ( select rownum AS "rn", t1.* from (select * from table where "some_field" is not null and "other_field" is null) t1 ) t2 where t2."rn" between 1 and 25

then takes 23 seconds in PHP or Oracle in our DB of 500k records.

Change to ROW_NUMBER() OVER

select t2.* from ( select ROW_NUMBER() OVER (ORDER BY t1.primary_key_field) AS "rn", t1.* from (select * from table where "some_field" is not null and "other_field" is null) t1 ) t2 where t2."rn" between 1 and 25

and it takes 0.8s.

GPT4 suggests:

protected function compileTableExpression($sql, $constraint, $query)
{
    // Case for fetching a single row
    if ($query->limit == 1 && is_null($query->offset)) {
        return "select * from ({$sql}) where rownum {$constraint}";
    }

    // Case for pagination with limit and offset
    if (!is_null($query->limit) && !is_null($query->offset)) {
        $start  = $query->offset + 1;
        $finish = $query->offset + $query->limit;

        // Using ROW_NUMBER() and CTE for efficient pagination
        return "
            WITH query_cte AS (
                SELECT t1.*, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
                FROM ({$sql}) t1
            )
            SELECT *
            FROM query_cte
            WHERE rn BETWEEN {$start} AND {$finish}
        ";
    }

    // Fallback for other cases
    return "
        WITH query_cte AS (
            SELECT t1.*, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
            FROM ({$sql}) t1
        )
        SELECT *
        FROM query_cte
        WHERE rn {$constraint}
    ";
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions