2011-11-01 7 views
5

Tengo una tabla tmp_drop_ids con una columna, id, y 3,3 millones de entradas. Quiero iterar sobre la mesa, haciendo algo con cada 200 entradas. Tengo este código:postgresql: desplazamiento + límite llega a ser muy 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) 

Esto funciona muy bien, al principio, (~ 0.15s para generar la tabla tmp), pero va a reducir la velocidad de vez en cuando, por ejemplo, alrededor de 300,000 entradas comenzaron a tomar 11-12 segundos para generar esta tabla de tmp, y de nuevo alrededor de 400k. Básicamente parece poco confiable.

Voy a utilizar esos identificadores en otras consultas, así que pensé que el mejor lugar para tenerlos estaba en una tabla tmp. ¿Hay alguna forma mejor de iterar a través de resultados como este?

+0

¿Tiene tmp_drop_ids indexado? CREAR UN ÍNDICE ÚNICO tmp_drop_ids_id_uidx ON tmp_drop_ids (id); – filiprem

+0

@filiprem: lo hago sí – Claudiu

Respuesta

9

Use un cursor en su lugar. Usar un DESPLAZAMIENTO y LÍMITE es bastante caro, porque pg tiene que ejecutar la consulta, procesar y omitir las filas de DESPLAZAMIENTO. OFFSET es como "omitir filas", que es caro.

cursor documentation

cursor permite una iteración sobre una consulta.

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

es probable que pueda utilizar un cursor del lado del servidor sin utilizar explícita de instrucción DECLARE, sólo con el apoyo de psycopg (sección de búsqueda de cursores del lado del servidor).

+0

Terminé haciendo esto desde python (usando el objeto del cursor 'fetchmany'). – Claudiu

2

Si es tu identificación se indexan puede utilizar "límite" con ">", por ejemplo en pseudocódigo pitón similar:

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") 

De esta manera Postgres puede utilizar el índice para su búsqueda.