0

たとえば、csvファイルからデータをインポートし、読み取ったデータをSQLテーブルに書き込むためのストアドプロシージャがあります。以下のように定義されたテーブルがあります。

CREATE TABLE person (id int, name text, age int, married boolean); 

まず、レコードがすでに存在するかどうかを確認します。存在する場合は更新し、存在しない場合は挿入します。レコードの各フィールドは異なるタイプである可能性があるため、SQLコマンドの結果はスカラー変数のリストに割り当てられます。

SELECT name, age, married INTO v_name, v_age, v_married [..]

すべての列がオプションとして宣言されていると仮定しましょう(NULLが許可されています)。次に、どの変数(v_name、v_age、v_married)がNULLでなく、処理できるかを確認するための最良の方法は何ですか?

私は多くの解決策を見つけました:

  • 見つからない場合
  • NO_DATA_FOUNDTHENの場合
  • v_ageがNULLでない場合[...]

または、複数の列(col)をチェックする必要がある場合に、上記の最後の方法を使用して現在使用している動的ソリューション:

list_of_columns := ARRAY['name','age','married'];
FOREACH x IN ARRAY list_of_columns LOOP
   EXECUTE 'SELECT ' || x
       || ' FROM person
            WHERE id = ' || quote_literal(v_id)
            INTO y;

   IF  x = 'name' AND (y != v_name OR y IS NULL) THEN
     UPDATE person
     SET    name = v_name
     WHERE  id = v_id;

   ELSIF x = 'age' AND (y != v_age OR y IS NULL) THEN
     UPDATE person
     SET    age = v_age
     WHERE  id = v_id;

   ELSIF x = 'married' AND (y != v_married OR y IS NULL) THEN
     UPDATE person
     SET    married= v_married
     WHERE  id = v_id;
   END IF;
END LOOP;

ベストプラクティスとパフォーマンスを考慮した最良のソリューションを探しています。どんな助けでも大歓迎です!

4

1 に答える 1

4

私は、これらの線に沿って手順全体を根本的に改善できると思います。

BEGIN;

CREATE TEMP TABLE tmp_p ON COMMIT DROP AS
SELECT * FROM person LIMIT 0;

COPY tmp_p FROM '/absolute/path/to/file' FORMAT csv;

UPDATE person p
SET    name    = t.name
      ,age     = t.age
      ,married = t.person
FROM   tmp_p t
WHERE  p.id = t.id
AND   (p.name    IS DISTINCT FROM t.name OR
       p.age     IS DISTINCT FROM t.age  OR
       p.married IS DISTINCT FROM t.married);

INSERT INTO person p(id, name, age, married, ...)
SELECT id, name, age, married, ...
FROM   tmp_p t
WHERE  NOT EXISTS (SELECT 1 FROM person x WHERE x.id = t.id);

COMMIT; -- drops temp table because of ON COMMIT DROP

説明

  • COPYCSVファイルを一致するレイアウトの一時テーブルに変換します。ターゲットテーブルのレイアウトをでコピーしましたCREATE TABLE AS ... LIMIT 0。適応する必要があるかもしれません...

  • UPDATE既存の行。WHERE句の最後の3行で空の更新(何も変更されない)を避けます。
    UPDATEでNULL値をスキップする場合(本当にそうですか?)、。のような式を使用しますCOALESCE(t.name, p.name)。の場合、これは既存の値にフォールバックしますNULL(役に立つかもしれませんが、それは実際にはあなたのコードにはありませんでした。)

  • INSERT存在しない行。NOT EXISTSそのために半結合を使用します。

  • オールインワントランザクションなので、途中で問題が発生した場合に結果が中途半端になることはありません。一時テーブルは、そのように作成したため、トランザクションの最後に削除されます。

于 2012-10-21T23:52:52.653 に答える