web-dev-qa-db-ja.com

PostgreSQLの\ COPYが失敗しました "エラー:列" column name2 "のデータがありません"

大量のデータ、つまり何百万ものレコードをインポートしています。列のnull値が原因で、インポートが失敗していることがわかりました。レコードがない場合にインポートエラーをスキップして、ジョブを実行し続ける方法はありますか?

1
Atty

PostgreSQLサーバーの\COPYコマンドは非常にシンプルで、単一の失敗で中止されます。あなたはそれがはるかに良くできると思うかもしれませんが(私は知っています)、PostgreSQLのコードベースがMySQLに対して(〜10/1の係数で)非常にコンパクトである理由があります。

ただし、別のユーティリティを実行する必要があるという代償を払ってこれを補正する(非常に)素晴らしい pgloader プログラムがあります。

もちろん、 PL/pgSQL 言語(サーバーの内部)に長けている場合は、そのルートを探索することもできますが、なぜ車輪を再発明するのでしょうか。 PythonおよびPerlには内部PostgreSQLオプションもあります。もちろん、Sunの下にはサーバーの外部にあるすべての言語があります。

manual: から

PgLoaderリファレンスマニュアル

pgloaderは、さまざまなソースからPostgreSQLにデータをロードします。オンザフライで読み取るデータを変換し、ロードの前後に生のSQLを送信できます。 COPY PostgreSQLプロトコルを使用してデータをサーバーにストリーミングし、reject.datファイルとreject.logファイルのペアを入力してエラーを管理します。

あなたの路地のすぐ上にあるように見えますか?

それが動作する方法は次のとおりです:( long quote

TL; DR-pgloaderは一度にバッチ(構成可能)をロードします。失敗すると、「スポットをマーク」し、そのポイントまで\COPYを再度使用して停止し、不良レコードをファイルに入れて、不良レコード+ 1から続行します。

バッチと再試行の動作

PostgreSQLにデータをロードするために、pgloaderはCOPYストリーミングプロトコルを使用します。これはデータをロードする高速な方法ですが、COPYには重要な欠点があります。PostgreSQLに送信されたデータのビットでエラーが発生するとすぐに、問題が何であれ、データセット全体がPostgreSQLによって拒否されます。

これを回避するために、pgloaderはデータをそれぞれ25000行のバッチにカットします。そのため、問題が発生した場合、その数のデータ行にのみ影響を与えます。 COPYストリーミングが行われている間、各バッチはメモリに保持されます。これは、エラーが発生した場合に処理できるようにするためです。

PostgreSQLがバッチ全体を拒否すると、pgloaderはエラーメッセージをログに記録し、バッチ処理された行を小さいバッチで再試行することにより、受け入れられた行から不良行を分離します。これを行うには、次の例のように、メッセージにバッチでエラーが検出された行番号がメッセージに含まれているため、pgloaderは失敗したCOPYからのCONTEXTエラーメッセージを解析します。

コンテキスト:コピーエラー、行3、列b:「2006-13-11」

その情報を使用して、pgloaderはエラーのある行の前にバッチ内のすべての行を再ロードし、エラーのある行を拒否としてログに記録し、残りのバッチのロードを1回の試行でロードします。

拒否された行を含むロードの最後に、セットアップのターゲットデータベースと同じ名前のディレクトリの下のroot-dirの場所に2つのファイルが見つかります。ファイル名はターゲットテーブルであり、その拡張子は拒否されたデータの場合は.datであり、拒否されたデータに関する完全なPostgreSQLクライアント側のログを含むファイルの場合は.logです。

2
Vérace