Fonction PLSQL¶
nous allons utiliser les tables suivantes
CREATE TABLE clients
(
pkey SERIAL PRIMARY KEY ,
nom VARCHAR (50 ) NOT NULL ,
prenom VARCHAR (50 ) NOT NULL
);
CREATE TABLE commandes
(
pkey SERIAL PRIMARY KEY ,
-- la commande pointe sur un client
client_pkey INTEGER references clients NOT NULL ,
total NUMERIC (10 ,2 )
);
CREATE TABLE produits
(
pkey SERIAL PRIMARY KEY ,
-- la contrainte "prix_positif" impose un prix strictement positif
prix NUMERIC (10 ,2 ) CONSTRAINT prix_positif CHECK (prix > 0 ) NOT NULL ,
-- l’intitulé du produit doit être unique
label VARCHAR (50 ) UNIQUE NOT NULL
);
-- cette table indique la quantité d’un produit pour une commande
CREATE TABLE produit_commande
(
-- "ON DELETE CASCADE" indique que si la ligne de données
-- référencée est supprimée celle-ci doit l'être également
commande_pkey INTEGER references commandes ON DELETE CASCADE ,
produit_pkey INTEGER references produits ON DELETE CASCADE ,
-- la quantité doit être strictement positive
quantite INTEGER CONSTRAINT quantite_positive CHECK (quantite > 0 ) NOT NULL ,
-- la clé primaire est formée de la clé du produit et de la commande
PRIMARY KEY (produit_pkey, commande_pkey)
);
PostgreSQL permet de créer des programmes dans un langage de procédure. Ces programmes sont stockés et executés sur le serveur. Les avantages sont multiples :
Cela permet dans certains cas d’améliorer l’efficacité des traitements en évitant des aller et retour entre le PostgreSQL et le client de la base. Cela offre une grande souplesse d’utilisation et une plus grande abstraction par rapport à la base de données.
Comme exemple de procédure, réalisons une fonction qui retourne tous les produits d’une commandes et les quantités associées :
-- créer un type de donné correspondant à ligne de réponse
-- est nécessaire pour ce qui va suivre
CREATE TYPE produit_de_la_commande
AS (pkey INTEGER , prix NUMERIC (10 ,2 ), label VARCHAR (50 ), quantite INTEGER );
-- la fonction "produits_de_la_commande" retourne tout les
-- produits d’une commande choisie avec les quantités
CREATE OR REPLACE FUNCTION produits_de_la_commande(INTEGER )
RETURNS SETOF produit_de_la_commande AS
$BODY$
DECLARE
-- on déclare une variable de travail type "produit_de_la_commande"
produit produit_de_la_commande;
BEGIN
-- la clé de la commande est passé comme paramètre. Si ce paramètre n’est
-- nommmé il prend comme valeur $n. N étant la position du paramètre dans le prototype.
FOR produit IN SELECT produits.*, produit_commande.quantite FROM produits, produit_commande
WHERE commande_pkey=$1 and produit_pkey=produits.pkey
LOOP
-- cette syntaxe est nécessaire pour retourner plusieurs lignes
RETURN NEXT produit;
END LOOP;
RETURN ;
END ;
$BODY$
LANGUAGE 'plpgsql' ;
L’intérêt d’une telle fonction est assez faible, mais elle permet d’obtenir une abstraction intéressante que nous allons utiliser par la suite :
-- cette fonction calcul le total d’une commande, le mets à jour et le renvoi
CREATE OR REPLACE FUNCTION calcul_total(INTEGER , OUT commande_total NUMERIC ) AS
$BODY$
DECLARE
produit produit_de_la_commande;
BEGIN
commande_total=0.0 ;
FOR produit IN SELECT * FROM produits_de_la_commande($1)
LOOP
commande_total=commande_total+(produit.prix*produit.quantite);
END LOOP;
-- mise à jour
UPDATE commandes SET total=commande_total WHERE pkey=$1;
END ;
$BODY$
LANGUAGE 'plpgsql' ;
Les choses commencent à être intéressantes. Nous avons calculé le total de la commande à l’aide de produits_de_la_commande puis nous avons mis à jour la ligne de la table commande correspondante, enfin nous renvoyons le total de la commande via le protoype de la fonction avec le mots clé OUT.
Nous pouvons utiliser cette fonction de la manière suivante :
SELECT * FROM calcul_total(1);