Résumé
Vous apprendrez ici à faire exécuter à Postgresql des traitements en réponse à des événements se produisant sur les tables d'une base de données.
Table des matières
Qu'est-ce qu'un trigger ?
Exemple 1. trigger
Par exemple, si le trigger est attaché à l'événement "insertion" de la table "client", à chaque nouveau client, le trigger sera exécuté.Notez que la mise à jour ou la suppression d'une ligne dans une table sont aussi des événements qui peuvent déclencher des triggers.
Le langage dans lequel est écrit le déclencheur(trigger) varie d'un SGBD à l'autre. Oracle utilise le PL/SQL ou le langage Java et Postgresql utilise plusieurs langages: python, tcl/tk, ruby etc... ainsi qu'une variante du PL/SQL appelée "plpgsql" qui sera celui que nous allons utiliser.
Pour pouvoir utiliser un langage d'écriture de fonctions dans une base de donnée Postgresql, il faut activer le support du langage pour la base. Cette opération est à répéter pour chaque base qui le nécessite.
La commande est la suivante: createlang plpgsql nomdelabase -Unomdupropriétaire pour plus de détail, se référer au manuel de createlang : man createlang
Nous allons créer un déclencheur attaché à l'événement "insertion d'un nouveau client'. Ce trigger va générer une valeur pour le numéro du nouveau client : Il est tout d'abord nécessaire de créer une procédure stockée:
CREATE FUNCTION gen_cle_client () RETURNS OPAQUE AS ' DECLARE nocli integer; BEGIN select into nocli max(no_client) from client; IF nocli ISNULL THEN nocli:=0; END IF; NEW.no_client:=nocli+1; RETURN NEW; END; ' LANGUAGE 'plpgsql'; |
Puis on va créer le trigger.
CREATE TRIGGER trig_bef_ins_client BEFORE INSERT ON client FOR EACH ROW EXECUTE PROCEDURE gen_cle_client(); |
A titre indicatif, voici l'équivalent sous Oracle:
Create trigger generation for client Before insert Declare variable plusGros integer; As Begin Select max(no_cli) from client into :plusGros; New.no_cli:=plusGros+1; End; |
CREATE FUNCTION gen_cle_client () RETURNS OPAQUE AS |
Le code qui suit est comme vous pouvez le constater délimité par des simples quotes, ce qui signifie que lorsqu'on a à placer des simples quotes à l'intérieur, il faudra prendre soin de les doubler.
' DECLARE nocli integer; |
BEGIN |
select into nocli max(no_client) from client; |
IF nocli ISNULL THEN |
nocli:=0; |
END IF; NEW.no_client:=nocli+1; |
RETURN NEW; |
END; ' LANGUAGE 'plpgsql'; |
Dans le code SQL ci-dessous on attache l'exécution de la fonction à l'événement et à la table auxquels il s'applique.
CREATE TRIGGER trig_bef_ins_client BEFORE INSERT ON client FOR EACH ROW EXECUTE PROCEDURE gen_cle_client(); |
Il s'agit de conserver dans une table "histo_salaires" l'historique des salaires des salariés de l'entreprise.
CREATE FUNCTION gerehisto () RETURNS OPAQUE AS ' DECLARE nocli integer; BEGIN IF NEW.salaire<>OLD.salaire THEN INSERT into hist_sal values(NEW.nom,NEW.prenom,''NOW'',OLD.salaire,NEW.salaire); END IF; RETURN NEW; END; ' LANGUAGE 'plpgsql'; CREATE TRIGGER trig_bef_update_employe BEFORE update ON salarie FOR EACH ROW EXECUTE PROCEDURE gerehisto(); |
Une procédure stockée est une fonction stockée dans la base de données. Lorsqu'on l'utilise, om met en oeuvre le client serveur de traitements. Voici une super fonction qui renvoie le double d'un entier passé en paramètre:
CREATE FUNCTION double (integer) RETURNS integer AS ' BEGIN RETURN 2*$1; END; ' LANGUAGE 'plpgsql'; |
Voici maintenant comment vous pouvez l'employer
select double(2); |
Voici un autre exemple de procédure contenant cette fois une requête:
CREATE FUNCTION get_pu_pdt (INTEGER) RETURNS FLOAT AS ' DECLARE lePu FLOAT; BEGIN SELECT INTO lePu pu FROM pdt WHERE no_pdt= $1; RETURN lePu; END ; ' LANGUAGE 'plpgsql'; |
Exercice 1: Créez une fonction qui renvoie le nom d'un client dont le numéro est passé en paramètre.
Exercice 2: écrire le trigger postgresql équivalent au trigger oracle situé ci-dessous
Create trigger set_pu for ldc before insert As Begin msg=new.NO_PDT; select pu from get_pu_pdt(:msg) into :NEW.pu; end ; |
Cette partie, est un essais de synthèse de la doc officielle. Se référer pour la documentation complète au site: www.postgresql.org
Toutes les variables doivent être déclarées dans la section prévue à cet effet
DECLARE >>>>>>>>>>c'est ici BEGIN END. |
Exemple 2. déclaration des variables
no_pdt integer; pi CONSTANT real:=3.14; nomclient varchar:="Dupond"; |
Lorsqu'une procédure stockée reçoit des paramètres en entrée, par exemple plusgrand(integer,integer) les paramètres sont accessibles dans l'ordre par respectivement $1 et $2. On pourra avantageusement les renommer comme ceci:
DECLARE nombre1 ALIAS FOR $1; nombre2 ALIAS FOR $2; |
On peut déclarer des variables composées correspondant à une ligne d'une table. Elles se comportent alors comme les structures du C.
Exemple 3. enregistrements
DECLARE monCli Client%rowtype; BEGIN return monCli.age+10; |
Les paramètres reçus par une fonction peuvent être aussi des tuples. On pourra dans ce cas accéder à $1.nomclient etc.... à moins que l'on ai créé des alias sur ceux-ci auquel cas on utilisera "nomd'alias.nomduchamp".
Exemple 4. affectation
identifiant:=expression ou valeur; |
Exemple 5. Select into
Le résultat d'un select renvoyant 1 enregistrement de plusieurs champs peut être affecté à une variable de type tuple ou à une liste de variables simples séparées par des virgulesDECLARE monCli client%ROWTYPE; nocli integer; nomcli varchar; BEGIN select into monCli * from client where nom_client='dupond'; ou select into nocli,nomcli * from client where nom_client=''dupond''; |
SELECT INTO monEnregistrement * FROM EMP WHERE nomEmp = ''Dupond''; IF NOT FOUND THEN RAISE EXCEPTION ''employé % non trouvé'', ''Dupond''; END IF; |
DECLARE users_rec RECORD; full_name varchar; BEGIN SELECT INTO users_rec * FROM users WHERE user_id=3; IF users_rec.homepage IS NULL THEN -- user entered no homepage, return "http://" ; END IF; END; |
On retrouvera les mêmes fonctions qu'avec mysql (hormis le préfixe qui change: "mysql_" est à remplacer par "pg_"). Voici un exemple classique:
<?php
$conn = pg_connect ("host=localhost port=5432 dbname=commerce user=dupond password=zG54Jds3!");
if (!$conn)
{
echo "Une erreur est survenue.\n";
exit;
}
else //connexion ok
{
$listeContacts = pg_exec($conn, "SELECT * FROM contact");
if (!$listeContacts)
{
echo "Une erreur est survenue.\n";
exit;
}
else //requête effectuée
{
$nbContacts = pg_numrows($listeContacts);
//Pour chaque contact ...
for ($noContact=0; $noContact<$num; $noContact++)
{
$enrContact = pg_fetch_row($listeContacts, $noContact);
//affichage du contact n° noContact
for ($noChamp=0; $noChamp<count($enrContact); $noChamp++)
{
echo "$enrContact[$noChamp] ";
}
}
//retour à la ligne après chaque contact
echo "<br>";
}
}
?>
|
Il s'agit de réaliser une base de donnée commerciale contenant les tables suivantes: "produit", "commande", et "ligne de commande". Tous les montants doivent se mettre à jour automatiquement. Ainsi que les quantités en stock. On ne pourra de plus pas enregistrer une ligne de commande que l'on ne pourrait satisfaire pour cause de stock insuffisant. Il s'agit d'une vue partielle de l'application. Notez bien que l'on ne gère pas ici les clients, ni les entrées en stock.
#Table produit Create Table pdt(no_pdt integer not null primary key, design_pdt varchar(50) ,pu float not null, stock integer default 0); #Table commande Create Table commande(no_com integer not null primary key, date_com date,montant float default 0); #Table Ligne de commande Create Table ldc ( no_cde integer not null, qte integer not null, no_pdt integer not null, pu float, montant COMPUTED BY (qte* pu), Primary key(no_pdt,nocde), foreign key (no_cde) references commande(no_com), foreign key (no_pdt) references pdt(no_pdt) ); |
Les Déclencheurs