5

少し「アップサート」タイプの質問があります...しかし、stackoverflowで読んだものとは少し異なるので、そこに投げたいと思います。

基本的な問題。

mysqlからPostgreSQL9.1.5(Herokuでホストされている)への移行に取り組んでいます。その一環として、毎日複数のCSVファイルをインポートする必要があります。一部のデータは販売情報であり、ほぼ確実に新しいものであり、挿入する必要があります。ただし、データの他の部分はほぼ同じであることが保証されています。たとえば、csvファイル(複数形に注意)には、POS(販売時点情報管理)情報が含まれます。これはめったに変更されません(そして、ほとんどの場合、追加によってのみ変更されます)。次に、製品情報があります。約10,000の製品があります(大部分は変更されませんが、追加と更新の両方を行うことができます)。

最後の項目(ただし重要です)は、特定の項目の監査証跡/情報を提供できるようにする必要があるということです。たとえば、新しいPOSレコードを追加する場合は、それが見つかったファイルまでさかのぼることができる必要があります。UPCコードまたは製品の説明を変更する場合は、それをさかのぼることができる必要があります。変更元のインポート(およびファイル)へ。

私が考えている解決策。

データはCSVで提供されるので、私はCOPYが最良/最速の方法であるという考えに取り組んでいます。ファイル内のデータの構造は、私がデータベースに持っているもの(つまり、最終的な宛先)とは正確には異なります。そのため、CSVに一致するステージングスキーマのテーブルにそれらをコピーしています(注:データソースごとに1つのスキーマ)。ステージングスキーマのテーブルには、挿入前の行トリガーがあります。これらのトリガーは、データの処理(挿入、更新、または無視)を決定できます。

新しいデータが含まれている可能性が最も高いテーブルの場合、最初に挿入を試みます。レコードがすでに存在する場合は、NULLを返します(ステージングテーブルへの挿入を停止します)。めったに変更されないテーブルの場合、テーブルにクエリを実行し、レコードが見つかったかどうかを確認します。もしそうなら、フィールドのいずれかが変更されているかどうかを確認する方法が必要です。(覚えておいてください、ファイルyからインポートxによってレコードが変更されたことを示す必要があります)明らかに、コードをボイラープレートして各列をテストすることができます。しかし、それよりももう少し「雄弁」で保守しやすいものを探していました。

ある意味、私がやっていることは、輸入システムと監査証跡システムを組み合わせることです。そこで、監査証跡を調査する際に、次のwiki.postgresql.orgの記事を確認しました。hstoreは変更を取得するための優れた方法のようです(そして、重要ではないテーブル内のいくつかの列を簡単に無視できるようになります-たとえば、 "last_modified")

私はそれがすべてうまくいくと約90%確信しています...私はいくつかのテストテーブルなどを作成し、それで遊んだことがあります。

私の質問?

データベースへの変更が必要な10Kからおそらく3つのレコードを見つけるというこのタスクを実行するための、より優れた、より保守しやすい方法です。私は確かにファイルを読み取り、各レコードをどう処理するかを理解しようとするPythonスクリプト(または他の何か)を書くことができましたが、それはひどく非効率的であり、多くのラウンドトリップにつながります。

最後にいくつか:

  1. 入力ファイルを制御できません。彼らが私にデルタを送ってくれればいいのですが、そうではなく、それは完全に私のコントロールや影響力の範囲外です。
  2. システムは成長しており、処理されるデータの量を大幅に増やす新しいデータソースが追加される可能性があります(したがって、私は物事を効率的に保つようにしています)
  3. これは素晴らしく単純なSOの質問(「Pythonでリストを並べ替える方法」など)ではないことは知っていますが、SOの優れた点の1つは、難しい質問をすることができ、人々が自分の考えを共有できることです。それを解決する最良の方法はです。
4

1 に答える 1

7

似たような操作がたくさんあります。私がしていることはCOPY一時的なステージングテーブルです。

CREATE TEMP TABLE target_tmp AS
SELECT * FROM target_tbl LIMIT 0;  -- only copy structure, no data

COPY target_tmp FROM '/path/to/target.csv';

パフォーマンスのために、-tempを実行しANALYZEます。テーブルはautovacuumによって分析されません!

ANALYZE target_tmp; 

また、パフォーマンスのために、一時テーブルに1つまたは2つのインデックスを作成するか、データで許可されている場合は主キーを追加することもできます。

ALTER TABLE ADD CONSTRAINT target_tmp_pkey PRIMARY KEY(target_id);

少量のインポートにはパフォーマンス関連のものは必要ありません。

次に、SQLコマンドの全範囲を使用して、新しいデータをダイジェストします。
たとえば、ターゲットテーブルの主キーが.である場合target_id

多分もうDELETE何がないのですか?

DELETE FROM target_tbl t
WHERE NOT EXISTS (
   SELECT 1 FROM target_tmp t1
   WHERE  t1.target_id = t.target_id
);

次にUPDATE、すでにそこにあるもの:

UPDATE target_tbl t
SET    col1 = t1.col1
FROM   target_tmp t1
WHERE  t.target_id = t1.target_id

空のUPDATEを回避するには、以下を追加するだけです。

...
AND    col1 IS DISTINCT FROM t1.col1; -- repeat for relevant columns

または、行全体が関連している場合:

...
AND    t IS DISTINCT FROM t1;         -- check the whole row

次にINSERT、新機能:

INSERT INTO target_tbl(target_id, col1)
SELECT t1.target_id, t1.col1
FROM   target_tmp t1
LEFT   JOIN target_tbl t USING (target_id)
WHERE  t.target_id IS NULL;

セッションが続行する場合はクリーンアップします(セッションの終了時に一時テーブルは自動的に削除されます):

DROP TABLE target_tmp;

またはON COMMIT DROP、と同様に使用しCREATE TEMP TABLEます。
コードはテストされていませんが、タイプミスを除いて、最新バージョンのPostgreSQLで動作するはずです。

于 2012-09-19T21:38:59.060 に答える