sql >> データベース >  >> RDS >> PostgreSQL

plpgsqlのトリガー関数の複数の列を更新します

    @Garyの答えは技術的には正しいものですが、PostgreSQLが行うとは言及していません。 このフォームをサポートする:

    UPDATE tbl
    SET (col1, col2, ...) = (expression1, expression2, ..)
    

    UPDATEのマニュアルを読む もう一度。

    彼が動的SQLを使いこなすのはまだ難しい。指定しなかったので、ビューが基になるテーブルと同じ列で構成されている単純なケースを想定しています。

    CREATE VIEW tbl_view AS SELECT * FROM tbl;
    

    問題

    • スペシャルレコードNEW EXECUTE内には表示されません 。 NEWを渡します USINGを使用した単一のパラメータとして EXECUTEの句 。

    • 説明したように、UPDATE リスト形式では、個別のが必要です 。副選択を使用して、レコードを個々の列に分割します:

      UPDATE ...
      FROM  (SELECT ($1).*) x
      

      $1の周りの括弧 オプションではありません。)これにより、string_agg()で作成された2つの列リストを簡単に使用できます。 カタログテーブルから:1つはテーブル修飾あり、もう1つはテーブル修飾なし。

    • 行の値全体を個々の列に割り当てることはできません。マニュアル:

      標準によれば、ターゲット列名の括弧で囲まれたサブリストのソース値は、正しい列数を生成する任意の行値の式にすることができます。 PostgreSQLでは、ソース値を行コンストラクターまたはサブSELECTにすることしかできません。 。

    • INSERT より簡単に実装されます。ビューとテーブルの構造が同じであると仮定して、列定義リストを省略します。 (改善される可能性があります。以下を参照してください。)

    解決策

    私はあなたのアプローチを輝かせるためにいくつかの更新を行いました。

    UPDATEのトリガー関数 :

    CREATE OR REPLACE FUNCTION f_trg_up()
      RETURNS TRIGGER AS
    $func$
    DECLARE
       tbl  text := quote_ident(TG_TABLE_SCHEMA) || '.'
                 || quote_ident(substring(TG_TABLE_NAME from '(.+)_view$'));
       cols text;
       vals text;
    BEGIN
       SELECT INTO cols, vals
              string_agg(quote_ident(attname), ', ')
             ,string_agg('x.' || quote_ident(attname), ', ')
       FROM   pg_attribute
       WHERE  attrelid = tbl::regclass
       AND    NOT attisdropped   -- no dropped (dead) columns
       AND    attnum > 0;        -- no system columns
    
       EXECUTE format('
       UPDATE %s t
       SET   (%s) = (%s)
       FROM  (SELECT ($1).*) x
       WHERE  t.id = ($2).id'
       , tbl, cols, vals) -- assuming unique "id" in every table
       USING NEW, OLD;
    
       RETURN NEW;
    END
    $func$ LANGUAGE plpgsql;
    

    INSERTのトリガー関数 :

    CREATE OR REPLACE FUNCTION f_trg_ins()
      RETURNS TRIGGER AS
    $func$
    DECLARE
        tbl text := quote_ident(TG_TABLE_SCHEMA) || '.'
                 || quote_ident(substring(TG_TABLE_NAME from '(.+)_view$'));
    BEGIN
       EXECUTE 'INSERT INTO ' || tbl || ' SELECT ($1).*'
       USING NEW;
    
       RETURN NEW;  -- don't return NULL unless you know what you're doing
    END
    $func$ LANGUAGE plpgsql;
    

    トリガー:

    CREATE TRIGGER trg_instead_up
    INSTEAD OF UPDATE ON a_view
    FOR EACH ROW EXECUTE PROCEDURE f_trg_up();
    
    CREATE TRIGGER trg_instead_ins
    INSTEAD OF INSERT ON a_view
    FOR EACH ROW EXECUTE PROCEDURE f_trg_ins();
    

    SQLフィドル INSERTのデモンストレーション およびUPDATE

    主なポイント

    • テーブル参照を明確にするためにスキーマ名を含めます。複数のスキーマの同じデータベースに同じテーブル名の複数のインスタンスが存在する可能性があります!

    • クエリpg_attribute information_schema.columnsの代わりに 。移植性は劣りますが、多く より速く、テーブルOIDを使用できるようになります。

      • 特定のスキーマにテーブルが存在するかどうかを確認する方法
    • テーブル名はSQLiに対して安全ではありません 動的SQLのクエリを作成する場合のように文字列として処理される場合。 quote_ident()でエスケープする またはformat() またはオブジェクト識別子タイプを使用します。これには、特別なトリガー関数変数TG_TABLE_SCHEMAが含まれます およびTG_TABLE_NAME

    • オブジェクト識別子タイプregclassにキャストします テーブル名が有効であることを表明し、カタログルックアップのOIDを取得します。

    • オプションでformat()を使用します 動的クエリ文字列を安全に構築します。

    • カタログテーブルの最初のクエリに動的SQLは必要ありません。より速く、よりシンプルに。

    • RETURN NEWを使用する RETURN NULLの代わりに 何をしているのかわからない限り、これらのトリガー関数で。 (NULL INSERTをキャンセルします 現在の行の場合。)

    • この単純なバージョンでは、すべてのテーブル(およびビュー)にidという名前の一意の列があることを前提としています。 。より洗練されたバージョンでは、主キーを動的に使用する場合があります。

    • UPDATEの関数 ビューとテーブルの列を任意の順序にすることができます 、セットが同じである限り。INSERTの関数 ビューとテーブルの列が同一の順序であることが期待されます 。任意の順序を許可する場合は、列定義リストをINSERTに追加します UPDATEと同じようにコマンド 。

    • 更新されたバージョンは、idへの変更もカバーしています OLDを使用した列 さらに。



    1. OracleODP.NetおよびEFCodeFirstと連携するようにDbContextを構成するにはどうすればよいですか。

    2. KubernetesでGaleraクラスターを実行する

    3. 主キー違反エラーの後にトランザクションを続行する

    4. EntityFramework7とAsp.Net5を使用してSQLストアドプロシージャを呼び出すにはどうすればよいですか