Dans PostgreSQL 10.10, j'ai créé une fonction de déclenchement dans PL/PGSQL qui convertit la ligne NEW
à un objet JSON à l'aide de to_jsonb(NEW)
. Mais maintenant, je dois inclure les enregistrements de l'autre côté des clés étrangères de l'enregistrement NEW
dans l'objet JSON de manière imbriquée.
Par exemple:
avant:
employee = {
"id": 1,
"name": "myname",
"department": 2,
"phone_no": "123456789"
}
après:
employee = {
"id": 1,
"name": "myname",
"department": {
"id": 2,
"name": "IT"
},
"phone_no": "123456789"
}
Quelle est la meilleure et la plus générique de manière générique de l'accomplir sans acquisition préalable sur le schéma de l'enregistrement NEW
? Je dois conserver cette fonction de déclenchement aussi générique que possible parce que je prévois d'l'utiliser sur toutes les tables. Un niveau de profondeur dans les clés étrangères suivantes me suffit actuellement. Aussi à simplifier, je peux supposer que toutes les clés étrangères devraient être une seule colonne.
Si je comprends bien, j'ai besoin de boucler sur toutes les colonnes de l'enregistrement NEW
, découvrez si la colonne est une clé étrangère utilisant information_schema
ou alors pg_catalog
, trouvez les détails de la clé étrangère telles que la colonne sur laquelle il pointe de la table, puis effectuez un SQL dynamique SELECT
(car je suppose que les noms de table et colonne seraient des chaînes, pas des identificateurs SQL) sur la table cible Pour l'enregistrement cible, convertissez l'enregistrement en JSON et l'attribuez-la enfin à la clé appropriée de l'objet JSON à niveau supérieur.
J'essaie encore d'écrire le code de travail actuel pour cela, pour lequel je me félicite de l'aide ou des indications. Et il pourrait y avoir des solutions plus simples à ce problème, que j'aimerais savoir.
Votre hypothèse est assez proche, vous aurez besoin de SQL dynamique.
Mais cela devrait être considérablement plus rapide et plus élégant que de boucler à travers toutes les colonnes de l'enregistrement NEW
record, etc.:
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;
Exemple de déclenchement à l'aide de la fonction ci-dessus:
CREATE TRIGGER upd_bef_jsonb_row_with_fk
BEFORE UPDATE ON tbl
FOR EACH ROW EXECUTE PROCEDURE trg_jsonb_row_with_fk();
Cela construit des sous-sols pour tous les FKS à partir de Tables de catalogue Postgres et exécute la commande SQL de manière dynamique. Pour la simplicité, j'inclus toutes les colonnes utilisateur des lignes correspondantes dans les tables de recherche (t.*
).
Nitpick: jsonb_build_object(%1$L, t.*)
, pas jsonb_build_object(%1$L, t)
.
[.____] On dirait que l'ajout de bruit, mais cela évite un problème de cas d'angle: Ceci est censé fonctionner pour tout table d'entrée, et on pourrait contenir une colonne nommée t
. Ensuite, le nom t
dans l'expression ci-dessus résoudrait à la colonne au lieu de l'alias de table (la ligne complète). L'utilisation de t.*
enlève cette ambiguïté car elle ne peut résoudre que toute la ligne. (Les parenthèses seraient nécessaires pour se référer à un type composite colonne, comme (t).*
). Lisez le manuel ici et ici .
Comme il utilise le nom de la colonne des colonnes FK d'origine comme nom de clé pour les objets étendus, la plaine Concaténation avec ||
Fait savoir ce dont vous avez besoin: elle remplace la valeur simple existante avec l'objet JSONB.
En plus de lecture: