308

Postgres データベースに数千万のレコードをプログラムで挿入する必要があります。現在、1 つのクエリで何千もの挿入ステートメントを実行しています。

これを行うためのより良い方法はありますか、私が知らない一括挿入ステートメントはありますか?

4

11 に答える 11

265

PostgreSQL には、最初にデータベースにデータを入力する最適な方法に関するガイドがあり、行の一括読み込みにCOPYコマンドを使用することを提案しています。このガイドには、データをロードする前にインデックスと外部キーを削除する (そして後で追加し直す) など、プロセスを高速化する方法に関するその他のヒントがいくつかあります。

于 2009-04-17T03:57:23.213 に答える
27

高速化する 1 つの方法は、トランザクション内で複数の挿入またはコピーを明示的に実行することです (たとえば、1000)。Postgres のデフォルトの動作は、各ステートメントの後にコミットすることです。そのため、コミットをバッチ処理することで、オーバーヘッドをいくらか回避できます。ダニエルの回答のガイドにあるように、これを機能させるには自動コミットを無効にする必要がある場合があります。また、wal_buffers のサイズを 16 MB に増やすことを提案する下部のコメントにも注意してください。

于 2009-04-17T04:06:48.090 に答える
12

「テキスト形式や CSV 形式よりも若干高速COPY table TO ... WITH BINARY」な方をご利用いただけます。これは、挿入する行が数百万あり、バイナリ データに慣れている場合にのみ行ってください。

バイナリ入力で psycopg2 を使用した Python のレシピの例を次に示します。

于 2011-11-17T09:33:08.173 に答える
10

それは主に、データベース内の (その他の) アクティビティに依存します。このような操作は、他のセッションのためにデータベース全体を効果的にフリーズします。別の考慮事項は、データモデルと、制約、トリガーなどの存在です。

私の最初のアプローチは常に: ターゲット テーブル ( ) に似た構造を持つ (一時) テーブルを作成しcreate table tmp AS select * from target where 1=0、ファイルを一時テーブルに読み込むことから始めます。次に、チェックできるものをチェックします: 重複、ターゲットに既に存在するキーなど。

次に、do insert into target select * from tmpまたは同様のことを行います。

これが失敗した場合、または時間がかかりすぎる場合は、中止して他の方法を検討します (一時的にインデックス/制約を削除するなど)。

于 2011-09-03T17:50:48.640 に答える
10

      ((これは編集して回答を強化できる WIKI です!))

外部ファイルは最適かつ典型的なバルク データです。

「バルクデータ」という用語は「大量のデータ」に関連しているため、SQL に変換する必要がなく、元の生データを使用するのが自然です。「一括挿入」の一般的な生データ ファイルは、CSVおよびJSON形式です。

いくつかの変換を伴う一括挿入

ETLアプリケーションと取り込みプロセスでは、挿入する前にデータを変更する必要があります。一時テーブルは (大量の) ディスク領域を消費しますが、これは高速な方法ではありません。PostgreSQL 外部データ ラッパー( FDW) が最適です。

CSV の例。on tablename (x, y, z)SQL と次のような CSV ファイルがあるとします。

fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...

クラシック SQL を使用して(元のデータのように) をCOPYにロードし、フィルター処理されたデータを...に挿入できますが、ディスクの消費を避けるためには、tmp_tablenametablename

INSERT INTO tablename (x, y, z)
  SELECT f1(fieldname1), f2(fieldname2), f3(fieldname3) -- the transforms 
  FROM tmp_tablename_fdw
  -- WHERE condictions
;

FDW 用にデータベースを準備する必要があり、代わりにそれを生成する関数をtmp_tablename_fdw静的に使用できます。

CREATE EXTENSION file_fdw;
CREATE SERVER import FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE tmp_tablename_fdw(
  ...
) SERVER import OPTIONS ( filename '/tmp/pg_io/file.csv', format 'csv');

JSON の例。2 つのファイルのセットで、myRawData1.json次のRanger_Policies2.json方法で取り込むことができます。

INSERT INTO tablename (fname, metadata, content)
 SELECT fname, meta, j  -- do any data transformation here
 FROM jsonb_read_files('myRawData%.json')
 -- WHERE any_condiction_here
;

関数jsonb_read_files()は、マスクで定義されたフォルダーのすべてのファイルを読み取ります。

CREATE or replace FUNCTION jsonb_read_files(
  p_flike text, p_fpath text DEFAULT '/tmp/pg_io/'
) RETURNS TABLE (fid int,  fname text, fmeta jsonb, j jsonb) AS $f$
  WITH t AS (
     SELECT (row_number() OVER ())::int id, 
           f as fname,
           p_fpath ||'/'|| f as f
     FROM pg_ls_dir(p_fpath) t(f)
     WHERE    f like p_flike
  ) SELECT id,  fname,
         to_jsonb( pg_stat_file(f) ) || jsonb_build_object('fpath',p_fpath),
         pg_read_file(f)::jsonb
    FROM t
$f$  LANGUAGE SQL IMMUTABLE;

gzip ストリーミングの欠如

「ファイルの取り込み」(主にビッグデータ) の最も一般的な方法は、元のファイルを gzip 形式で保存し、ストリーミング アルゴリズムを使用して転送することです。これは、Unix パイプでディスクを消費せずに高速で実行できるものなら何でも:

 gunzip remote_or_local_file.csv.gz | convert_to_sql | psql 

したがって、理想的な(将来)は、format のサーバーオプション.csv.gzです。

@CharlieClark コメントの後の注意: 現在 (2022) 何もする必要はありません。最良の代替手段はpgloaderSTDINのようです:

  gunzip -c file.csv.gz | pgloader --type csv ... - pgsql:///target?foo
于 2020-06-21T01:28:34.797 に答える
5

この問題に遭遇したばかりで、Postgres への一括インポートにはcsvsql ( releases ) をお勧めします。一括挿入を実行するには、単純createdbに を使用csvsqlして、データベースに接続し、CSV のフォルダー全体に対して個々のテーブルを作成します。

$ createdb test 
$ csvsql --db postgresql:///test --insert examples/*.csv
于 2015-08-13T15:08:49.440 に答える