@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
を使用した列 さらに。