12

処理してリモートデータベースに保存するデータファイルが大量にあります。データファイルの各行はデータベースの行を表しますが、データベースに挿入する前にフォーマットする必要があります。

私の最初の解決策は、bashスクリプトを記述してデータファイルを処理し、SQLデータファイルを生成してから、ダンプSQLファイルをデータベースにインポートすることでした。このソリューションは遅すぎるようで、ご覧のとおり、中間SQLファイルを作成する追加の手順が必要です。

私の2番目の解決策は、データファイルの各行を処理しながらINSERT INTO ...、SQLステートメントを作成してリモートデータベースに送信するbashスクリプトを作成することでした。

echo sql_statement | psql -h remote_server -U username -d database

つまり、SQLファイルを作成しません。ただし、このソリューションには、アドバイスを探している1つの大きな問題があり
ます。リモートデータベースに再接続して1つの行を挿入する必要があるたびに。

巨大なSQLファイルを作成せずに、リモートデータベースに接続し、接続を維持してから、insert-SQL-statementを「パイプ」または「送信」する方法はありますか?

4

2 に答える 2

20

あなたの実際の質問に答えてください

はい。ファイルを作成する代わりに、名前付きパイプを使用できます。次のデモを検討してください。

テスト用xにデータベースにスキーマを作成します。event

-- DROP SCHEMA x CASCADE;
CREATE SCHEMA x;
CREATE TABLE x.x (id int, a text);

次のように、シェルから名前付きパイプ(fifo)を作成します。

postgres@db:~$ mkfifo --mode=0666 /tmp/myPipe

1)サーバー上のCOPY名前付きパイプを使用してSQLコマンドを呼び出します。

postgres@db:~$ psql event -p5433 -c "COPY x.x FROM '/tmp/myPipe'"

これにより、データベース内のテーブルの排他ロックが取得x.xされます。fifoがデータを取得するまで、接続は開いたままになります。これを長時間開いたままにしないように注意してください!パイプを満たした後でこれを呼び出して、ブロッキング時間を最小限に抑えることができます。イベントの順序を選択できます。このコマンドは、2つのプロセスがパイプにバインドされるとすぐに実行されます。最初は2番目を待ちます。

または2)クライアントのパイプからSQLを実行できます:

postgres@db:~$ psql event -p5433 -f /tmp/myPipe

これはあなたのケースにより適しています。また、SQLが1つのピースで実行されるまで、テーブルはロックされません。

Bashはブロックされたように見えます。パイプへの入力を待っています。1つのbashインスタンスからすべてを実行するには、代わりに待機プロセスをバックグラウンドに送信できます。このような:

postgres@db:~$ psql event -p5433 -f /tmp/myPipe 2>&1 &

いずれにせよ、同じbashまたは別のインスタンスから、パイプを埋めることができます。
バリアント1)の3行のデモ:

postgres@db:~$ echo '1  foo' >> /tmp/myPipe; echo '2    bar' >> /tmp/myPipe; echo '3    baz' >> /tmp/myPipe;

(タブを区切り文字として使用するか、を使用して別の区切り文字を受け入れるようにCOPYWITH DELIMITER 'delimiter_character'に指示するように注意してください)
これにより、COPYコマンドで保留中のpsqlがトリガーされ、実行されて返されます。

COPY 3

バリアント2のデモ)

postgres@db:~$ (echo -n "INSERT INTO x.x VALUES (1,'foo')" >> /tmp/myPipe; echo -n ",(2,'bar')" >> /tmp/myPipe; echo ",(3,'baz')" >> /tmp/myPipe;)

INSERT 0 3

完了したら、名前付きパイプを削除します。

postgres@db:~$ rm /tmp/myPipe

成功を確認します。

event=# select * from x.x;
 id |         a
----+-------------------
  1 | foo
  2 | bar
  3 | baz

上記のコードの便利なリンク

名前付きパイプを使用したpostgresでの圧縮ファイルの読み取り
名前付きパイプの概要
バックグラウンドでbashスクリプトを実行するためのベストプラクティス


あなたが必要とするかもしれないし、必要としないかもしれないアドバイス

バルクの場合、行ごとに個別のINSERTINSERTを使用するよりも優れたソリューションがあります。この構文バリアントを使用します。

INSERT INTO mytable (col1, col2, col3) VALUES
 (1, 'foo', 'bar')
,(2, 'goo', 'gar')
,(3, 'hoo', 'har')
...
;

ステートメントをファイルに書き込み、次のINSERTように1つのマスを実行します。

psql -h remote_server -U username -d database -p 5432 -f my_insert_file.sql

(5432またはdb-clusterがリッスンしているポート)
my_insert_file.sqlは、複数のSQLステートメントを保持できます。実際、そのようなデータベース全体を復元/展開するのが一般的な方法です。パラメータについて、またはbashでマニュアルを-f参照してくださいman psql

または、(圧縮された)ファイルをサーバーに転送できる場合は、COPYを使用して(解凍された)データをさらに高速に挿入できます。

PostgreSQL内で処理の一部またはすべてを実行することもできます。そのために、一時テーブルを作成し、プレーンSQLステートメントを使用してテーブルを準備し、最後にINSERT/UPDATEを実行できCOPY TOます。INSERT INTO私はそれをたくさんします。一時テーブルはセッションとともに存続し、消滅することに注意してください。

快適な操作のためにpgAdminのようなGUIを使用することができます。SQLエディタウィンドウのセッションは、ウィンドウを閉じるまで開いたままになります。(したがって、一時テーブルはウィンドウを閉じるまで存続します。)

于 2011-10-21T14:16:22.617 に答える
1

INSERTパーティーに遅れていることは知っていますが、すべてのステートメントを1つの文字列にまとめて、各ステートメントの終わりをセミコロンでマークできないのはなぜですか。(警告!先に擬似コード...)

それ以外の:

for each line
  sql_statement="INSERT whatever YOU want"
  echo $sql_statement | psql ...
done

使用する:

sql_statements=""
for each line
  sql_statement="INSERT whatever YOU want;"
  sql_statements="$sql_statements $sql_statement"
done
echo $sql_statements | psql ...

そうすれば、ファイルシステム上に何も作成したり、大量のリダイレクトを実行したり、バックグラウンドでタスクを実行したり、後でファイルシステム上のものを削除したり、名前付きパイプが何であるかを思い出したりする必要がありません。

于 2013-06-13T04:25:01.940 に答える