回答:
PostgreSQLには、データベースに最初にデータを追加する方法についてのガイドがあり、行を一括ロードするためにCOPYコマンドを使用することを推奨しています。このガイドには、データをロードする前にインデックスと外部キーを削除する(そして後で追加する)など、プロセスを高速化する方法に関する他の優れたヒントがいくつかあります。
Postgresがサポートする複数行の値の構文であるCOPYの使用に代わるものがあります。ドキュメントから:
INSERT INTO films (code, title, did, date_prod, kind) VALUES
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');
上記のコードは2つの行を挿入しますが、準備されたステートメントトークンの最大数に達するまで、任意に拡張できます($ 999になる可能性がありますが、100%確実ではありません)。時にはCOPYを使用できないことがあり、これはそれらの状況の価値ある置き換えです。
速度を上げる1つの方法は、トランザクション内で明示的に複数の挿入またはコピーを実行することです(たとえば1000)。Postgresのデフォルトの動作は、各ステートメントの後にコミットすることです。そのため、コミットをバッチ処理することにより、オーバーヘッドを回避できます。ダニエルの回答のガイドにあるように、これを機能させるには自動コミットを無効にする必要がある場合があります。また、下部にあるコメントで、wal_buffersのサイズを16 MBに増やすと役立つ場合があることにも注意してください。
UNNEST
配列を持つ関数は、複数行のVALUES構文とともに使用できます。私はこの方法は使用するよりも遅いと思いますがCOPY
、psycopgとpython(python がpgにlist
渡される)を操作するのに役立ちます:cursor.execute
ARRAY
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
);
VALUES
追加の存在チェックで副選択を使用しない場合:
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
SELECT * FROM (
SELECT UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
) AS temptable
WHERE NOT EXISTS (
SELECT 1 FROM tablename tt
WHERE tt.fieldname1=temptable.fieldname1
);
一括更新と同じ構文:
UPDATE tablename
SET fieldname1=temptable.data
FROM (
SELECT UNNEST(ARRAY[1,2]) AS id,
UNNEST(ARRAY['a', 'b']) AS data
) AS temptable
WHERE tablename.id=temptable.id;
COPY table TO ... WITH BINARY
「テキストやCSV形式よりもやや速い」を使用できます。これを行うのは、挿入する行が数百万あり、バイナリデータに慣れている場合のみです。
以下は、バイナリ入力でpsycopg2を使用したPythonのレシピの例です。
それは主にデータベース内の(その他の)アクティビティに依存します。このような操作は、他のセッションのためにデータベース全体を事実上凍結します。別の考慮事項は、データモデルと、制約、トリガーなどの存在です。
私の最初のアプローチは常に:ターゲットテーブルに似た構造の(一時)テーブルを作成し(テーブルtmp AS select * from target where 1 = 0)、ファイルを一時テーブルに読み込むことから始めます。次に、何をチェックできるかを確認します。重複、ターゲットに既に存在するキーなどです。
次に、「ターゲット選択* tmpから挿入を実行する」などを実行します。
これが失敗した場合、または時間がかかりすぎる場合は、中止して他の方法(一時的にインデックス/制約を削除するなど)を検討します
ネイティブのlibpqメソッドを使用して非常に高速なPostgresqデータローダーを実装しました。私のパッケージを試してくださいhttps://www.nuget.org/packages/NpgsqlBulkCopy/
「バルクデータ」という用語は「大量のデータ」に関連しているため、SQLに変換する必要なく、元の生データを使用するのが自然です。「一括挿入」の一般的な生データファイルはCSVとJSONです形式形式です。
でETLのアプリケーションや摂取プロセス、我々はそれを挿入する前に、データを変更する必要があります。一時テーブルはディスクスペースを(大量に)消費しますが、それを行うための高速な方法ではありません。PostgreSQLの外部データラッパ(FDW)が最良の選択です。
CSVの例。仮定tablename (x, y, z)
SQLとCSVファイルなどに
fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...
従来のSQL COPY
を使用して(元のデータと同じように)にロードしtmp_tablename
、フィルターされたデータをtablename
...に挿入できます。ただし、ディスクの消費を回避するには、
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形式で保持し、それをストリーミングアルゴリズムで転送することです。これは、UNIXパイプでディスクを消費せずに高速で実行できるものです。
gunzip remote_or_local_file.csv.gz | convert_to_sql | psql
したがって、理想的な(将来の)は、formatのサーバーオプションです .csv.gz
。