Est-ce possible? Je souhaite savoir quelles colonnes ont été spécifiées dans la demande UPDATE
, indépendamment du fait que la nouvelle valeur envoyée peut être ou ne pas être celle déjà stockée dans la base de données.
La raison pour laquelle je veux faire cela est parce que nous avons une table qui peut recevoir des mises à jour de plusieurs sources. Auparavant, nous n'enregistrions pas la source de la mise à jour. Maintenant, la table stocke quelle source a effectué la mise à jour la plus récente. Nous pouvons changer certaines sources pour envoyer un identifiant, mais ce n'est pas une option pour tout. J'aimerais donc pouvoir reconnaître le moment où une demande UPDATE
ne possède pas d'identifiant afin de pouvoir lui substituer une valeur par défaut.
Si une "source" ne "envoie pas d'identifiant", la colonne reste inchangée. Ensuite, vous ne pouvez pas déterminer si la UPDATE
actuelle a été créée par la même source que la dernière ou par une source qui n'a pas changé la colonne du tout. En d'autres termes: cela ne fonctionne pas correctement.
Si la "source" est identifiable par une fonction d'information session , vous pouvez l'utiliser. Comme:
NEW.column = session_user;
Inconditionnellement pour chaque mise à jour.
J'ai trouvé un moyen de résoudre le problème initial. La colonne sera définie sur une valeur par défaut dans any update où la colonne est non mis à jour (pas dans la liste SET
de la UPDATE
).
L'élément clé est un déclencheur par colonne introduit dans PostgreSQL 9.0 - un déclencheur spécifique à la colonne utilisant la clause UPDATE OF
column_name
.
Le déclencheur ne se déclenchera que si au moins une des colonnes répertoriées est mentionnée comme cible de la commande
UPDATE
.
C'est le seul moyen simple que j'ai trouvé pour distinguer si une colonne a été mise à jour avec une nouvelle valeur identique à l'ancienne, par rapport à pas du tout mise à jour.
Un pourrait analyser également le texte renvoyé par current_query()
. Mais cela semble difficile et peu fiable.
Je suppose une colonne col
défini NOT NULL
.
Étape 1: Définissez col
sur NULL
si inchangé:
CREATE OR REPLACE FUNCTION trg_tbl_upbef_step1()
RETURNS trigger AS
$func$
BEGIN
IF OLD.col = NEW.col THEN
NEW.col := NULL; -- "impossible" value
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Étape 2: Revenir à l'ancienne valeur. Le déclencheur sera ne sera déclenché que si la valeur a été réellement mise à jour (voir ci-dessous):
CREATE OR REPLACE FUNCTION trg_tbl_upbef_step2()
RETURNS trigger AS
$func$
BEGIN
IF NEW.col IS NULL THEN
NEW.col := OLD.col;
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Étape 3: Nous pouvons maintenant identifier la mise à jour manquante et définir une valeur par défaut à la place:
CREATE OR REPLACE FUNCTION trg_tbl_upbef_step3()
RETURNS trigger AS
$func$
BEGIN
IF NEW.col IS NULL THEN
NEW.col := 'default value';
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Le déclencheur de étape 2 est déclenché par colonne!
CREATE TRIGGER upbef_step1
BEFORE UPDATE ON tbl
FOR EACH ROW
EXECUTE PROCEDURE trg_tbl_upbef_step1();
CREATE TRIGGER upbef_step2
BEFORE UPDATE OF col ON tbl -- key element!
FOR EACH ROW
EXECUTE PROCEDURE trg_tbl_upbef_step2();
CREATE TRIGGER upbef_step3
BEFORE UPDATE ON tbl
FOR EACH ROW
EXECUTE PROCEDURE trg_tbl_upbef_step3();
Les noms de déclencheurs sont pertinents, car ils sont déclenchés par ordre alphabétique (tous étant BEFORE UPDATE
)!
La procédure peut être simplifiée avec quelque chose comme "Déclencheurs par colonne" ou tout autre moyen de vérifier la liste de cibles d'une UPDATE
dans un déclencheur. Mais je ne vois aucune poignée pour cela.
Si col
peut être NULL
, utilisez une autre valeur intermédiaire "impossible" et recherchez également NULL
dans la fonction de déclenchement 1:
IF OLD.col IS NOT DISTINCT FROM NEW.col THEN
NEW.col := '#impossible_value#';
END IF;
Adapter le reste en conséquence.
Dans plpgsql, vous pourriez faire quelque chose comme ceci dans votre fonction de déclencheur:
IF NEW.column IS NULL THEN
NEW.column = 'default value';
END IF;
J'ai obtenu une autre solution à un problème similaire presque naturellement, car mon tableau contenait une colonne avec la sémantique de 'last update timestamp' (appelons-le UPDT).
J'ai donc décidé d'inclure de nouvelles valeurs de source et UPDT dans toute mise à jour uniquement à la fois (ou aucune d'entre elles). Étant donné que l'UPDT doit changer à chaque mise à jour, une telle stratégie permet d'utiliser la condition new.UPDT = old.UPDT
pour déduire qu'aucune source n'a été spécifiée avec la mise à jour actuelle et remplacer celle par défaut.
Si on a déjà la colonne 'dernière mise à jour' dans sa table, cette solution sera plus simple que de créer trois déclencheurs. Pas sûr qu'il soit préférable de créer un UPDT, alors que ce n'est pas déjà nécessaire. Si les mises à jour sont si fréquentes qu'il existe un risque de similitude d'horodatage, un séquenceur peut être utilisé à la place de l'horodatage.