PostgreSQL 10.10では、NEW
行をto_jsonb(NEW)
を使用してJSONオブジェクトに変換するトリガー関数をPL/pgSQLで作成しました。しかし、今度は、NEW
レコードの外部キーの反対側にあるレコードを、ネストされた方法でJSONオブジェクトに含める必要があります。
例えば:
前:
employee = {
"id": 1,
"name": "myname",
"department": 2,
"phone_no": "123456789"
}
後:
employee = {
"id": 1,
"name": "myname",
"department": {
"id": 2,
"name": "IT"
},
"phone_no": "123456789"
}
NEW
レコードのスキーマに関する事前の知識なしにこれを達成するための最良かつ最も一般的な方法は何ですか?すべてのテーブルで使用する予定なので、このトリガー関数をできるだけ汎用的にする必要があります。現在のところ、外部キーの追跡の深さは1レベルで十分です。また、簡単にするために、すべての外部キーは単一の列であると想定できます。
私が理解しているように、NEW
レコードのすべての列をループして、列がinformation_schema
またはpg_catalog
を使用して外部キーであるかどうかを調べ、次のような外部キーの詳細を見つける必要があります。ターゲットテーブルのどの列を指し、次にターゲットレコードのターゲットテーブルに対して動的SQL SELECT
を実行します(テーブルと列名はSQL識別子ではなく文字列であるため)、レコードをJSONに変換します最後に、それを最上位行のJSONオブジェクトの適切なキーに割り当てます。
私はまだこのための実際の作業コードを記述しようとしています。そのためのヘルプや指示は歓迎します。そして、私が知りたい、この問題のより簡単な解決策があるかもしれません。
推測はかなり近いです。動的SQLが必要になります。
しかし、これはNEW
レコードなどのすべての列をループするよりもはるかに高速でエレガントなはずです。
_CREATE OR REPLACE FUNCTION trg_jsonb_row_with_fk()
RETURNS trigger AS
$func$
DECLARE
_sql text;
_jsonb_row jsonb;
BEGIN
SELECT 'SELECT to_jsonb($1) || '
|| string_agg(
format('(SELECT jsonb_build_object(%1$L, t.*)
FROM %2$s t WHERE %3$I = $1.%1$I)'
, a.attname -- %1$L, %1$I
, c.confrelid::regclass -- %2$s
, f.attname) -- %3$I
, ' || ')
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid
JOIN pg_attribute f ON f.attrelid = c.confrelid
WHERE c.conrelid = TG_RELID
AND c.contype = 'f' -- to select only FK constraints
AND a.attnum = c.conkey[1] -- assuming only single-col FKs!
AND f.attnum = c.confkey[1]
INTO _sql;
IF FOUND THEN -- FKs found
EXECUTE _sql USING NEW INTO _jsonb_row;
ELSE -- no FKs found, plain conversion
_jsonb_row := to_jsonb(NEW);
END IF;
RAISE NOTICE '%', _jsonb_row; -- do something with it ...
RETURN NEW; -- proceed with org. row
END
$func$ LANGUAGE plpgsql;
_
上記の関数を使用したトリガーの例:
_CREATE TRIGGER upd_bef_jsonb_row_with_fk
BEFORE UPDATE ON tbl
FOR EACH ROW EXECUTE PROCEDURE trg_jsonb_row_with_fk();
_
これにより、 Postgresカタログテーブル からすべてのFKのサブクエリが作成され、SQLコマンドが動的に実行されます。簡単にするため、ルックアップテーブル(_t.*
_)の対応する行のすべてのユーザー列を含めます。
Nitpick:jsonb_build_object(%1$L, t.*)
、jsonb_build_object(%1$L, t)
ではありません。
ノイズを追加するように見えますが、コーナーケースの問題は回避されます。これはany入力テーブルで機能するはずであり、t
という名前の列が含まれる場合があります。次に、上記の式のt
という名前は、テーブルエイリアス(行全体)ではなくcolumnに解決されます。 _t.*
_を使用すると、行全体にしか解決できないため、このあいまいさが解消されます。 (括弧は、_(t).*
_のように、複合型columnを参照する必要があります)。マニュアル ここ および ここ をお読みください。
元のFK列の列名を拡張オブジェクトのキー名として使用するため、プレーンな _||
_ との連結は必要なことを実行します。既存の単純な値をjsonbオブジェクトに置き換えます。
参考文献: