数百万のタプルをデータベースに一括挿入する最も効率的な方法を探しています。私はPython、PostgreSQL、および psycopg2 を使用しています。
私はデータベースに挿入する必要のあるチュールプの長いリストを作成しました。幾何学的なSimplify
などの修飾子を使用することもあります。
それを行う素朴な方法は、INSERT
ステートメントのリストを文字列フォーマットすることですが、私が読んだ他の3つの方法があります。
pyformat
バインディングスタイル を使用するexecutemany
を使用し、COPY
を使用する。最初の方法が最も効率的ですが、正しい方法を教えてくれる洞察とコードスニペットをいただければ幸いです。
ああ、私はCOPYに投票します。ただし、COPYはサーバーを読み取るだけなので、ファイルをサーバーのハードドライブ(アプリが実行されているドライブではない)に書き込むことができます。
すべてのオプションの例を含む新しい psycopg2マニュアル があります。
[〜#〜] copy [〜#〜] オプションが最も効率的です。その後、executmany。次に、pyformatで実行します。
私の経験では、executemany
は多くの挿入を自分で実行するよりも速くはありません。最も速い方法は、単一のINSERT
を多数の値で自分でフォーマットすることです。将来的にはexecutemany
が向上するでしょう今のところかなり遅いです
list
をサブクラス化して、appendメソッドをオーバーロードします。これにより、リストが特定のサイズに達したときに、INSERTをフォーマットして実行します
新しいアップサートライブラリ を使用できます。
$ pip install upsert
(最初にpip install decorator
する必要がある場合があります)
conn = psycopg2.connect('dbname=mydatabase')
cur = conn.cursor()
upsert = Upsert(cur, 'mytable')
for (selector, setter) in myrecords:
upsert.row(selector, setter)
selector
は{'name': 'Chris Smith'}
のようなdict
オブジェクトであり、setter
は{ 'age': 28, 'state': 'WI' }
のようなdict
です
カスタムINSERT [/ UPDATE]コードを記述してpsycopg2
...で直接実行するのとほぼ同じ高速であり、行がすでに存在する場合は爆発しません。
SQLalchemyを使用している人なら誰でも、次のようにuse_batch_mode = Trueでエンジンを初期化するときに、executemanyではなくpsycopg2.extras.execute_batch()を使用する一括挿入のサポートを追加した1.2バージョンを試すことができます。
engine = create_engine(
"postgresql+psycopg2://scott:tiger@Host/dbname",
use_batch_mode=True)
http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109
次に、誰かがSQLalchmeyを使用する必要があり、sqlaとpsycopg2のさまざまな組み合わせを試してSQLを一緒に指示する必要はありません。
最初と2番目は別々にではなく一緒に使用されます。サーバーはallのハードワークを実行するため、3番目はサーバーとしては最も効率的です。
いくつかのテストの後、私は@ Clodoaldo Netoの-から学んだように、 nnest は非常に高速なオプションであるように見えることがよくあります- answer 同様の質問へ。
data = [(1, 100), (2, 200), ...] # list of tuples
cur.execute("""CREATE TABLE table1 AS
SELECT u.id, u.var1
FROM unnest(%s) u(id INT, var1 INT)""", (data,))
ただし、それは 非常に大きなデータの場合は注意が必要です です。
非常に関連する質問: SQLAlchemy ORMを使用した一括挿入
すべての道路はローマに通じていますが、いくつかは山を越えており、フェリーが必要ですが、そこにすばやく行きたい場合は高速道路を利用してください。
この場合、高速道路は execop_batch()psycopg2 の機能を使用します。ドキュメントはそれが最高だと言っています:
executemany()
の現在の実装は、(非常に慈善的な控えめな表現を使用して)特に実行されていません。これらの関数を使用すると、一連のパラメーターに対するステートメントの繰り返し実行を高速化できます。サーバーの往復回数を減らすことで、パフォーマンスはexecutemany()
を使用するよりも桁違いに向上します。
私自身のテストでは、execute_batch()
はexecutemany()
の約2倍の速さであり、page_sizeを構成するオプションを提供しますさらに微調整(ドライバーのパフォーマンスの最後の2〜3%を絞りたい場合)。
create_engine()
でエンジンをインスタンス化するときにパラメータとしてuse_batch_mode=True
を設定することにより、SQLAlchemyを使用している場合、同じ機能を簡単に有効にすることができます