web-dev-qa-db-ja.com

PostgresトリガーのFROM句でNEWを使用しますか?

新しい行を挿入または更新する前に、配列フィールドのネストを解除するPostgresトリガーを記述しようとしています。例えば。

SELECT
unnest(something)
FROM NEW

ただし、これによりエラーが発生するようです。

「新しい」関係は存在しません

トリガー関数の中でNEW行を使用して、配列フィールドのネストを解除してさらに処理できるようにするにはどうすればよいですか?

CREATE TABLEステートメント

以下は、テーブル構造の例です。

CREATE TABLE parent_table (
    id uuid NOT NULL,
    jsonb_array_field jsonb[] NOT NULL DEFAULT '{}'::jsonb[],
    CONSTRAINT "parent_table_pkey" PRIMARY KEY (id),
);
CREATE TABLE many_to_one_table (
  id serial primary key,
  parent_table_id uuid references parent_table(id),
  subfield_a text,
  subfield_a text
);

TRIGGER関数

TRIGGER関数の本質は次のとおりです。

CREATE OR REPLACE FUNCTION unnest_and_normalize_json() RETURNS TRIGGER AS 
$body$
begin
    if NEW.jsonb_array_field is null then
        raise exception 'jsonb_array_field is null';
    else
        insert into 
            many_to_one_table(subfield_a, subfield_b)
            select
                parent_table_id, -- this is to be the ForeignKey
                json_data->>'subfieldA' as subfield_a,
                json_data->>'subfieldB' as subfield_b
            from (
                select 
                    id, -- need ID for ForeignKey relationship
                    unnest(jsonb_array_field) as json_data
                    from new
            ) as unnested_json;
    end if;
    RETURN new;
end;
$body$ 
LANGUAGE plpgsql;

TRIGGERステートメント

トリガーステートメントは、データが「移行」テーブルにミラーリングされている限り、INSERTおよびUPDATEの前または後に実行できます。

CREATE TRIGGER unnest_and_normalize_json_on_insert AFTER INSERT ON parent_table
FOR EACH ROW EXECUTE PROCEDURE unnest_and_normalize_json();

CREATE TRIGGER unnest_and_normalize_json_on_update AFTER UPDATE ON parent_table
FOR EACH ROW EXECUTE PROCEDURE unnest_and_normalize_json();

ForeignKey clarificationデータモデルのこの部分を移行して、JSONフィールドではなくForeignKey関係を使用しようとしています。トリガーは、normalized_tableには今後のデータがありますが、古いレコードからデータをバックフィル/移行します。

NEWおよびOLD トリガー関数はrecords-それぞれ該当する場合にのみ定義されます。 (たとえば、OLDトリガーにはINSERTはありません。) ROW operations を実行できます。ネストされた列は、FROMリストに含まれているテーブルと同様に、ドット表記(テーブル修飾列名など)で参照します。 NEWOLDは、トリガー関数のSQL DMLステートメントのほぼすべての場所に表示されます。 (EXECUTEを使用した動的SQLを除く。)

NEW実際のテーブルとして扱うには、まず変換する必要があります-これは通常は不要です。例:(SELECT NEW.*)。意味のあるユースケースについては、この関連回答の章3。を参照してください。

基本的なトリガー機能は次のように燃え尽きます:

CREATE OR REPLACE FUNCTION unnest_and_normalize_json()
  RETURNS trigger AS 
$func$
BEGIN
   INSERT INTO many_to_one_table (parent_table_id, subfield_a, subfield_b)
   SELECT NEW.id
        , ja->>'subfieldA' AS subfield_a  -- column alias only for documentation
        , ja->>'subfieldB'
   FROM   unnest(NEW.jsonb_array_field) ja;  -- produces a derived table

   RETURN NEW; -- optional for AFTER trigger
END
$func$  LANGUAGE plpgsql;

これは単なるノイズでした:

if NEW.jsonb_array_field is null then ...

jsonb_array_fieldは次のように定義されます。

jsonb_array_field jsonb[] NOT NULL DEFAULT '{}'::jsonb[],

これはAFTERトリガーです。NOT NULL制約は、この時点でjsonb_array_fieldNULLの例外をすでに発生させています。

2つのトリガーを1つにマージできます。

CREATE TRIGGER unnest_and_normalize_json_on_insert
AFTER INSERT OR UPDATE ON parent_table  -- !
FOR EACH ROW EXECUTE PROCEDURE unnest_and_normalize_json();

とはいえ、UPDATEDELETEを適切にカバーするには、さらに多くのことを行う必要があります。これと同じ(章The dark side):

4