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 SELECT pour 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 :

  1. Une requête SQL exécutée directement dans psql.
  2. 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.

  1. Créez une procédure audit_capacites() sans argument.

  2. Parcourez tous les villages avec un curseur implicite.

  3. 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;
    
  4. 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 :

  1. Parcourir tous les séjours terminés avant p_date_limite via un curseur.

  2. Pour chaque séjour :

    • l’insérer dans ArchiveSejour (avec date_archivage = now()),
    • puis le supprimer de Sejour.
  3. À 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 :

  1. Parcourt tous les séjours du client donné avec un curseur implicite.

  2. 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;
  3. En sortie, afficher le total cumulé du client.

Partie C – En autonomie

  1. Fonction “villages_disponibles” avec curseur – Modifier votre fonction précédente pour vérifier la capacité de chaque village via un curseur.

  2. 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 idc donné.
    • Retourner {ids, idv, activité}.

Partie D – Synthèse et bonnes pratiques

  • Utiliser FOR IN SELECT dans 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.
Pierre-Henri Paris
Pierre-Henri Paris
Associate Professor in Artificial Intelligence

My research interests include Knowlegde Graphs, Information Extraction, and NLP.