E
E
Eugene TI2022-01-20 20:59:59
PostgreSQL
Eugene TI, 2022-01-20 20:59:59

Psycopg2: pagination of large requests?

Let there be a flask web application.
Base - PostgreSQL through psycopg2
And now a selection (SELECT) is made from a table or view.
The sample result can be unintentionally very large (some [hundreds] of millions of records).
The task is to find out the number of potentially received rows before actually fetching them .
Well and - if it is admissible - to organize pagination.
Like this:

@bp.route('/megaquery', methods=['GET'])
def megaquery():
    global conn, cur
    page = request.args.get('page', 1, type=int)
    if cur is None:
        cur = conn.cursor(name='coolquery', scrollable=True)
        cur.execute('SELECT * FROM megaview ORDER BY datime ASC')
    else:
        cur.scroll((page-1) * PAGE_SIZE, 'absolute')
    # вот в этом месте надо знать скока (cur.rowcount == -1)
    return render_template('megaquery.html', data=cur.fetchmany(PAGE_SIZE), paging=(page, cur.rowcount // PAGE_SIZE))

Answer the question

In order to leave comments, you need to log in

3 answer(s)
G
galaxy, 2022-01-20
@galaxy

The task is to find out the number of potentially received rows before actually fetching them.

onlySELECT count(*) ...
Well and - if it is admissible - to organize pagination.

And here it is necessary to look at the request and the plan. In simple cases (like SELECT * FROM table) fetching the first pages is very fast (especially through LIMIT). But it's easy to break, for example, by sorting

D
Dr. Bacon, 2022-01-20
@bacon

Well, often the total number of pages can be ignored, and instead of OFFSET, use where ID>last_id_on the previous page, etc.

S
ScriptKiddo, 2022-01-21
@ScriptKiddo

https://wiki.postgresql.org/wiki/Count_estimate
As advised in the comments - you can take it from the EXPLAIN query

CREATE FUNCTION count_estimate(query text)
  RETURNS integer
  LANGUAGE plpgsql AS
$func$
DECLARE
    rec   record;
    rows  integer;
BEGIN
    FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
        rows := substring(rec."QUERY PLAN" FROM ' rows=(+)');
        EXIT WHEN rows IS NOT NULL;
    END LOOP;

    RETURN rows;
END
$func$;

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question