2011-11-01 16 views
5

Ho una tabella tmp_drop_ids con una colonna, id e 3,3 milioni di voci. Voglio scorrere il tavolo, facendo qualcosa con ogni 200 voci. Ho questo codice:postgresql: offset + limite diventa molto lento

LIMIT = 200 
for offset in xrange(0, drop_count+LIMIT, LIMIT): 
    print "Making tmp table with ids %s to %s/%s" % (offset, offset+LIMIT, drop_count) 
    query = """DROP TABLE IF EXISTS tmp_cur_drop_ids; CREATE TABLE tmp_cur_drop_ids AS 
    SELECT id FROM tmp_drop_ids ORDER BY id OFFSET %s LIMIT %s;""" % (offset, LIMIT) 
    cursor.execute(query) 

Questo funziona benissimo, in un primo momento, (~ 0.15s per generare la tabella tmp), ma rallenterà di tanto in tanto, per esempio circa 300k biglietti ha iniziato a prendere 11-12 secondi per generare questo tavolo TMP, e ancora circa 400k. Sembra fondamentalmente inaffidabile.

Userò questi ID in altre query, quindi ho pensato che il posto migliore per trovarli fosse in una tabella tmp. C'è un modo migliore per scorrere attraverso risultati come questo?

+0

Hai indicizzato tmp_drop_ids? CREATE UNICO INDICE tmp_drop_ids_id_uidx ON tmp_drop_ids (id); – filiprem

+0

@filiprem: sì sì – Claudiu

risposta

9

Utilizzare invece un cursore. Usare OFFSET e LIMIT è piuttosto costoso, perché pg deve eseguire query, elaborare e saltare una riga OFFSET. OFFSET è come "salta righe", che è costoso.

cursor documentation

cursore permette un'iterazione su una query.

BEGIN 
DECLARE C CURSOR FOR SELECT * FROM big_table; 
FETCH 300 FROM C; -- get 300 rows 
FETCH 300 FROM C; -- get 300 rows 
... 
COMMIT; 

probabilmente si può utilizzare un cursore sul lato server senza l'utilizzo esplicito di DECLARE, proprio con supporto in psycopg (sezione di ricerca sui cursori lato server).

+0

ho finito per fare questo da python (usando 'fetchmany' dell'oggetto cursore). – Claudiu

2

Se l'ID del sono indicizzate è possibile utilizzare "limite" con ">", ad esempio nel pseudocodice python-like:

limit=200 
max_processed_id=-1 
query ("create table tmp_cur_drop_ids(id int)") 
while true: 
    query("truncate tmp_cur_drop_ids") 
    query("insert into tmp_cur_drop_ids(id)" \ 
     + " select id from tmp_drop_ids" \ 
     + " where id>%d order by id limit %d" % (max_processed_id, limit)) 
    max_processed_id = query("select max(id) from tmp_cur_drop_ids") 
    if max_processed_id == None: 
    break 
    process_tmp_cur_drop_ids(); 
query("drop table tmp_cur_drop_ids") 

In questo modo è possibile utilizzare Postgres indice per la query.