Bloc 4 — TD/TP : Curseurs et traitement séquentiel
Objectifs pédagogiques
- Comprendre l’utilité des curseurs en PL/pgSQL et leurs limites.
- Savoir déclarer, ouvrir, lire et fermer un curseur.
- Utiliser la boucle
FOR … IN SELECTpour un parcours implicite. - Appliquer ces concepts à la gestion des séjours (conformément au cahier des charges).
Partie A – Sur feuille
1. Lecture et compréhension
Expliquez la différence entre :
- Une requête SQL exécutée directement dans
psql. - Un parcours ligne à ligne via un curseur en PL/pgSQL.
Indiquez dans quel cas le curseur est justifié dans le contexte de l’application des séjours.
2. Structure d’un curseur explicite
Complétez le squelette suivant pour parcourir tous les villages et afficher leur nom et leur capacité :
DO $$
DECLARE
c_village CURSOR FOR SELECT ville, capacite FROM village;
v_ville text;
v_capacite int;
BEGIN
OPEN c_village;
LOOP
FETCH c_village INTO v_ville, v_capacite;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'Ville: %, Capacité: %', v_ville, v_capacite;
END LOOP;
CLOSE c_village;
END;
$$;
Commentez le rôle de chaque instruction (DECLARE, OPEN, FETCH, EXIT WHEN, CLOSE).
3. Variante implicite
Réécrivez le code précédent à l’aide d’une boucle implicite :
FOR rec IN
SELECT ville, capacite FROM village
LOOP
RAISE NOTICE 'Ville: %, Capacité: %', rec.ville, rec.capacite;
END LOOP;
Expliquez pourquoi cette version est préférable.
Partie B – Sur machine
Exercice 1 — Fonction “clients inactifs”
Écrire une fonction clients_sans_sejour() renvoyant la liste des clients sans séjour actuel :
CREATE OR REPLACE FUNCTION clients_sans_sejour()
RETURNS TABLE(idc bigint, nom text)
Utilisez un curseur implicite (FOR … IN SELECT) parcourant les clients, et un test via NOT EXISTS.
Exercice 2 — Procédure “audit des capacités”
Objectif : détecter les villages dont la capacité totale est saturée.
Créez une procédure
audit_capacites()sans argument.Parcourez tous les villages avec un curseur implicite.
Pour chaque village, comptez les séjours actifs à la date du jour :
SELECT count(*) INTO v_nb FROM sejour WHERE idv = rec.idv AND CURRENT_DATE BETWEEN debut AND fin;Affichez un message :
RAISE NOTICE 'Village % : % / % lits occupés', rec.ville, v_nb, rec.capacite;
Exercice 3 — Procédure “purge archivée”
Reprendre la logique de purge_sejours() (Bloc 3), mais :
Parcourir tous les séjours terminés avant
p_date_limitevia un curseur.Pour chaque séjour :
- l’insérer dans
ArchiveSejour(avecdate_archivage = now()), - puis le supprimer de
Sejour.
- l’insérer dans
À la fin, afficher le nombre total d’archives créées.
Exercice 4 — Procédure “bilan client”
Créer une procédure bilan_client(p_idc bigint) qui :
Parcourt tous les séjours du client donné avec un curseur implicite.
Pour chaque séjour :
- calcule la durée
(fin - debut)et le montant(fin - debut)*prix, - affiche
RAISE NOTICE 'Séjour % : % nuits, % €', ids, duree, montant;
- calcule la durée
En sortie, afficher le total cumulé du client.
Partie C – En autonomie
Fonction “villages_disponibles” avec curseur – Modifier votre fonction précédente pour vérifier la capacité de chaque village via un curseur.
Procédure “réservation automatique” – Implémenter la fonctionnalité 4 du cahier des charges :
- Parmi les villages disponibles, choisir le plus cher.
- Insérer le séjour pour
idcdonné. - Retourner
{ids, idv, activité}.
Partie D – Synthèse et bonnes pratiques
- Utiliser
FOR IN SELECTdans 95 % des cas. - Pattern recommandé pour les curseurs explicites :
LOOP → FETCH → EXIT WHEN. - Toujours fermer (
CLOSE) les curseurs ouverts manuellement. - Préférer SQL ensembliste dès que le traitement le permet.