web-dev-qa-db-ja.com

Psycopg2、Postgresql、Python:一括挿入する最速の方法

数百万のタプルをデータベースに一括挿入する最も効率的な方法を探しています。私はPython、PostgreSQL、および psycopg2 を使用しています。

私はデータベースに挿入する必要のあるチュールプの長いリストを作成しました。幾何学的なSimplifyなどの修飾子を使用することもあります。

それを行う素朴な方法は、INSERTステートメントのリストを文字列フォーマットすることですが、私が読んだ他の3つの方法があります。

  1. パラメトリック挿入に pyformatバインディングスタイル を使用する
  2. タプルのリストでexecutemanyを使用し、
  3. 結果をファイルに書き込み、COPYを使用する。

最初の方法が最も効率的ですが、正しい方法を教えてくれる洞察とコードスニペットをいただければ幸いです。

41
Adam Matan

ああ、私はCOPYに投票します。ただし、COPYはサーバーを読み取るだけなので、ファイルをサーバーのハードドライブ(アプリが実行されているドライブではない)に書き込むことができます。

14
Andy Shellam

すべてのオプションの例を含む新しい psycopg2マニュアル があります。

[〜#〜] copy [〜#〜] オプションが最も効率的です。その後、executmany。次に、pyformatで実行します。

10
piro

私の経験では、executemanyは多くの挿入を自分で実行するよりも速くはありません。最も速い方法は、単一のINSERTを多数の値で自分でフォーマットすることです。将来的にはexecutemanyが向上するでしょう今のところかなり遅いです

listをサブクラス化して、appendメソッドをオーバーロードします。これにより、リストが特定のサイズに達したときに、INSERTをフォーマットして実行します

8
FlashDD

新しいアップサートライブラリ を使用できます。

$ 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...で直接実行するのとほぼ同じ高速であり、行がすでに存在する場合は爆発しません。

7
Seamus Abshere

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
user2189731

最初と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,))

ただし、それは 非常に大きなデータの場合は注意が必要です です。

1
n1000

非常に関連する質問: SQLAlchemy ORMを使用した一括挿入


すべての道路はローマに通じていますが、いくつかは山を越えており、フェリーが必要ですが、そこにすばやく行きたい場合は高速道路を利用してください。


この場合、高速道路は execop_batch()psycopg2 の機能を使用します。ドキュメントはそれが最高だと言っています:

executemany()の現在の実装は、(非常に慈善的な控えめな表現を使用して)特に実行されていません。これらの関数を使用すると、一連のパラメーターに対するステートメントの繰り返し実行を高速化できます。サーバーの往復回数を減らすことで、パフォーマンスはexecutemany()を使用するよりも桁違いに向上します。

私自身のテストでは、execute_batch()executemany()の約2倍の速さであり、page_sizeを構成するオプションを提供しますさらに微調整(ドライバーのパフォーマンスの最後の2〜3%を絞りたい場合)。

create_engine()でエンジンをインスタンス化するときにパラメータとしてuse_batch_mode=Trueを設定することにより、SQLAlchemyを使用している場合、同じ機能を簡単に有効にすることができます

0
chjortlund