2011-11-01 6 views
5

Bir sütun, id ve 3,3 milyon giriş içeren bir tablo var tmp_drop_ids. Her 200 girişte bir şeyler yapıp masanın üzerinde yinelemek istiyorum. Bu kodu vardır: Bu ilk başta, iyi çalışırpostgresql: offset + limit çok yavaş olsun

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) 

, (~ 0.15s tmp tablo oluşturmak için) ama mesela bazen yavaşlatacaktır Yaklaşık 300 bin biletle bu tmp tablosunu oluşturmak için 11-12 saniye sürdü ve yine 400k civarında. Temelde güvenilmez gibi görünüyor.

Bu sorguları diğer sorgularda kullanacağım, bu yüzden bunların bir tmp tablosunda olması için en iyi yeri buldum. Bunun gibi sonuçlarla yinelemek için daha iyi bir yol var mı?

+0

Tmp_drop_ids dizinlendiniz mi? UNIQUE INDEX CREATE tmp_drop_ids_id_uidx ON tmp_drop_ids (id); – filiprem

+0

@filiprem: Evet – Claudiu

cevap

9

Bunun yerine bir imleç kullanın. Bir OFFSET ve LIMIT kullanmak oldukça pahalıdır - çünkü pg, bir OFFSET satırını sorgulamak, işlemek ve atlamak zorundadır. OFFSET, "satırları atla" gibi, pahalı.

cursor documentation

İmleç bir sorgunun üzerine bir denemeler yapılmasına olanak sağlar.

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

Muhtemelen sadece psycopg (sunucu tarafı imleçleri hakkında arama bölümünde) 'de desteği ile, açık DECLARE bildirimi kullanmadan bir sunucu tarafı imleci kullanabilirsiniz.

+0

Evet, bunu python'dan yapıyorum (imleç nesnesinin 'fetchmany' kullanarak). – Claudiu

2

sizin id Diyelim ki ">" ile "sınır" kullanabilirsiniz dizine, piton benzeri pseudocode örneğin:

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

Postgres sorgunuzla indeksi kullanabilirsiniz Bu şekilde.