2017-12-13 8 views
1

テーブルを更新するスクリプトを作成しました。 「一括更新」する方法が見つからなかったので、スクリプトは一度に1行ずつテーブルを更新します。私は、100,000行のセットについては、更新を行うのに数秒かかることを想定していました。なぜpsycopg2の書き込みに時間がかかるのですか?

いいえ。各書き込み操作には約100ミリ秒かかります。書き込み操作全体((((100,000 * 100)/ 1000)/ 60)/ 60)= 2.77時間です。なぜそれを書くのに時間がかかりますか?

import psycopg2 
... 
entries = get_all_entries() 
conn = psycopg2.connect(params) 
try: 
    for entry in entries: 
     cursor = conn.cursor() 
     cursor.execute(UPDATE_QUERY.format(entry.field1, entry.field2)) 
     cursor.close() 
finally: 
    conn.close() 

私が間違っているのは何:

ここで私が使用しているコードですか?

+0

1回のトランザクションの通常の安全性を持たないことを前提としている場合、 'conn.commit()'を数回(2500?)クエリごとに実行すると、トランザクションが大きすぎる(クエリが遅くなります) – Ryan

+1

高速化の方法:['copy_to()'](http://initd.org/psycopg/docs/usage.html#using-copy-to-and-copy-from)を使用してデータを一時テーブルにアップロードしてください。たとえば、[UPDATE ... FROM ...](https://www.postgresql.org/docs/current/static/sql-update.html)を使用して、単一のSQL文でテーブルを更新します。 – Abelisto

+0

@Abelisto興味深い。あなたはこれをどうやって行うのかと答えることができますか? – dopatraman

答えて

1

の代わりに使用して、サーバー側の一時テーブルにデータをアップロードするために、あなたが可能性、クライアント側からの行でテーブルの行を更新すると、このコードをプロファイルすることができますcopy_from()メソッドを呼び出して、単一のSQLでテーブルを更新します。あなたは第二の方法は、約20倍高速で見ることができるように

 
Prepare playground... 
Done. 

Test update row by row... 
Done in 62.1189928055 s. 

Test batch update... 
Done in 3.95668387413 s. 

#!/usr/bin/env python 

import time, psycopg2 
from random import random 
from cStringIO import StringIO 

CRowCount = 100000 

conn = psycopg2.connect('') 
conn.autocommit = False 

print('Prepare playground...') 
cur = conn.cursor() 
cur.execute(""" 
    drop table if exists foo; 
    create table foo(i int primary key, x float); 
    insert into foo select i, 0 from generate_series(1,%s) as i; 
""", (CRowCount,)) 
print('Done.') 
cur.close(); 
conn.commit(); 

print('\nTest update row by row...') 
tstart = time.time() 
cur = conn.cursor() 
for i in xrange(1,CRowCount+1): 
    cur.execute('update foo set x = %s where i = %s', (random(), i)); 
conn.commit() 
cur.close() 
print('Done in %s s.' % (time.time() - tstart)) 

print('\nTest batch update...') 
tstart = time.time() 
cur = conn.cursor() 
# Create temporary table to hold our data 
cur.execute('create temp table t(i int, x float) on commit drop') 
# Create and fill the buffer from which data will be uploaded 
buf = StringIO() 
for i in xrange(1,CRowCount+1): 
    buf.write('%s\t%s\n' % (i, random())) 
buf.seek(0) 
# Upload data from the buffer to the temporary table 
cur.copy_from(buf, 't') 
# Update test table using data previously uploaded 
cur.execute('update foo set x = t.x from t where foo.i = t.i') 
cur.close(); 
conn.commit(); 
print('Done in %s s.' % (time.time() - tstart)) 

が出力:ここ

は人工的な例です。

+0

ありがとう!なぜこれがより速いのか少し説明できますか? – dopatraman

+0

@dopatramanほとんどの場合、100,000個のSQL文を実行する代わりに、3回だけ実行するため、 – Abelisto

+0

これらの行を具体的に説明できますか? (1、CRowCount + 1)内のi: buf = StringIO() buf.write( '%s \')は、 buf.seek(0) cur.copy_from(buf、 't') – dopatraman

関連する問題