PostgreSQL La base de donnees la plus sophistiquee au monde.

Forums PostgreSQL.fr

Le forum officiel de la communauté francophone de PostgreSQL

Vous n'êtes pas identifié(e).

#1 11/10/2016 16:20:32

bl4n
Membre

[Trigger] Récupérer l'ID d'une entrée soit insérée, soit sélectionnée

Bonjour,

Étant débutant en TRIGGER, je me permet de solliciter un coup de pouce afin de résoudre ce problème qui me tracasse depuis quelques temps.

Voici un schéma résumé de ce sur quoi je travaille : http://dbpatterns.com/documents/57fceed … 3da5724af/

Ce que j'aimerais obtenir :

  • Remplir le champ intervention_id de la table equipment_history

  • Faire ce remplissage au moment de l'insertion d'une ligne dans equipment_history (MAIS je ne sais pas si je dois faire un Trigger BEFORE ou AFTER l'insertion)

Ce qu'il faut savoir sur le champ intervention_id :

  • Si une entrée existe déjà pour le couple date/place_id donnée, on utilise cet ID

  • Sinon on insère une nouvelle intervention et on utilise cet ID pour remplir le champ intervention_id

Jusqu'à maintenant j'ai tenté d'utiliser un truc comme ça :

---- TRIGGER générique qui copie les données d'une table vers sa table "_history"

CREATE OR REPLACE FUNCTION gissmo_audit()
            RETURNS trigger AS $body$
            DECLARE
                name TEXT;
            BEGIN
                IF (TG_OP = 'UPDATE') THEN
                    name := TG_TABLE_NAME::TEXT || '_history';
                    EXECUTE format('INSERT INTO %I VALUES(($1).*)', name) USING OLD;
                    RETURN NEW;
                END IF;
            EXCEPTION
                WHEN data_exception THEN
                    RAISE WARNING '[gissmo_audit] - ERROR [DATA EXCEPTION] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
                WHEN unique_violation THEN
                    RAISE WARNING '[gissmo_audit] - ERROR [UNIQUE] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
                WHEN OTHERS THEN
                    RAISE WARNING '[gissmo_audit] - ERROR [OTHER] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
                    RETURN NULL;
            END;
            $body$ LANGUAGE plpgsql;

---- Ajout du TRIGGER générique sur la table equipment_equipment

CREATE TRIGGER equipment_equipment_audit
            AFTER UPDATE ON equipment_equipment
            FOR EACH ROW EXECUTE PROCEDURE gissmo_audit();

---- TRIGGER censé remplir le champ intervention_id lors de l'insertion d'une ligne d'historique (NE FONCTIONNE PAS :'()

CREATE OR REPLACE FUNCTION have_place_id_field_clustering()
            RETURNS trigger AS $body$
            DECLARE
                intervention_id INTEGER;
                given_date DATE;
            BEGIN
                IF (TG_OP = 'INSERT') AND (NEW.intervention_id IS NULL) THEN
                    given_date := date_trunc('day', NEW.insert_datetime);
                    INSERT INTO intervention_intervention AS intervention (id, date, place_id, confirmed) VALUES (DEFAULT, given_date, NEW.place_id, False)
                        ON CONFLICT (date, place_id) DO UPDATE SET
                            confirmed = 'f'
                        RETURNING id
                    INTO intervention_id;
                    NEW.intervention_id := (SELECT id FROM intervention_intervention WHERE date = given_date AND place_id = NEW.place_id LIMIT 1);
                    RETURN NEW;
                END IF;
            EXCEPTION
                WHEN data_exception THEN
                    RAISE WARNING '[have_place_id_field_clustering] - ERROR [DATA EXCEPTION] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
                WHEN unique_violation THEN
                    RAISE WARNING '[have_place_id_field_clustering] - ERROR [UNIQUE] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
                WHEN OTHERS THEN
                    RAISE WARNING '[have_place_id_field_clustering] - ERROR - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
                    RETURN NULL;
            END;
            $body$ LANGUAGE plpgsql;

Je pense n'être pas loin du bon résultat, mais je crains d'être un peu trop simplet pour aligner les choses correctement et atteindre un résultat probant.

Je vous remercie d'avance pour toute l'aide que vous pourriez m'apporter à ce sujet et vous souhaite une agréable journée,

bl4n alias Personne.

Hors ligne

#2 11/10/2016 16:44:22

Marc Cousin
Membre

Re : [Trigger] Récupérer l'ID d'une entrée soit insérée, soit sélectionnée

Le premier truc qui m'embête dans le trigger:

                IF (TG_OP = 'INSERT') AND (NEW.intervention_id IS NULL) THEN
                    given_date := date_trunc('day', NEW.insert_datetime);
                    INSERT INTO intervention_intervention AS intervention (id, date, place_id, confirmed) VALUES (DEFAULT, given_date, NEW.place_id, False)
                        ON CONFLICT (date, place_id) DO UPDATE SET
                            confirmed = 'f'
                        RETURNING id
                    INTO intervention_id;
                    NEW.intervention_id := (SELECT id FROM intervention_intervention WHERE date = given_date AND place_id = NEW.place_id LIMIT 1);
                    RETURN NEW;
                END IF;

Si on ne passe pas dans ce IF, la fonction est censée faire quoi ? il faut qu'elle fasse un RETURN de quelque chose dans ce cas là.

le mieux c'est de faire des RAISE NOTICE un peu partout pour tracer où le trigger passe…

Hors ligne

#3 11/10/2016 17:18:27

bl4n
Membre

Re : [Trigger] Récupérer l'ID d'une entrée soit insérée, soit sélectionnée

En effet, je n'avais pas vu cela comme ça étant donné que je ne prévoyais pas d'appeler le trigger autrement que pour un INSERT.

Je pense, mais peut-être que je me trompe, qu'un RETURN NULL devrait convenir, un peu comme ça :

                IF (TG_OP = 'INSERT') AND (NEW.intervention_id IS NULL) THEN
                    given_date := date_trunc('day', NEW.insert_datetime);
                    INSERT INTO intervention_intervention AS intervention (id, date, place_id, confirmed) VALUES (DEFAULT, given_date, NEW.place_id, False)
                        ON CONFLICT (date, place_id) DO UPDATE SET
                            confirmed = 'f'
                        RETURNING id
                    INTO intervention_id;
                    NEW.intervention_id := (SELECT id FROM intervention_intervention WHERE date = given_date AND place_id = NEW.place_id LIMIT 1);
                    RETURN NEW;
                ELSE
                    RETURN NULL;
                END IF;

Qu'en pensez-vous ?

Je n'avais pas pensé à utiliser des RAISE NOTICE dans mon trigger. Ça va me permettre de faire du debug et voir ce qui cloche, merci !

Hors ligne

#4 11/10/2016 18:07:55

Marc Cousin
Membre

Re : [Trigger] Récupérer l'ID d'une entrée soit insérée, soit sélectionnée

Ça semble un bon début.

Sinon, il y a aussi raise debug qui est intéressant… il faut faire log_min_messages à debug ou client_min_messages à debug pour faire apparaître les messages, c'est intéressant quand on veut pouvoir fournir des traces de debug dans du code PL qui ne soit pas actif en permanence.

Hors ligne

#5 14/10/2016 15:43:05

bl4n
Membre

Re : [Trigger] Récupérer l'ID d'une entrée soit insérée, soit sélectionnée

Bon, finalement je crois avoir trouvé l'ensemble des soucis que j'avais :

1/ Au départ je n'utilisais pas "id" dans la ligne d'INSERT, donc le RETURNING ne renvoyait pas l'id
2/ Plus la peine d'utiliser une sous requête si on récupère bien l'id dans "intervention_id"
3/ Ajout du ELSE par précaution
4/ Lorsque j'affecte le TRIGGER à une table, je faisais un "AFTER INSERT" et ça n'enregistrait pas la valeur dans la colonne. C'est désormais le cas. Voici l'ajout du trigger :

CREATE TRIGGER equipment_equipment_cluster
            BEFORE INSERT ON equipment_equipment_history
            FOR EACH ROW EXECUTE PROCEDURE have_place_id_field_clustering();

Maintenant que j'ai réglé ces petits soucis, il va me falloir faire de même pour plusieurs autre cas un tout petit peu plus corsés big_smile

Hors ligne

Pied de page des forums