DO $$
DECLARE
x int;
y text;
BEGIN
SELECT a, b INTO x, y
FROM t
WHERE cle = 123;
RAISE NOTICE 'Valeurs : % %', x, y;
END;
$$;
x et y.graph LR
A[Requête SQL] -->|OPEN| B[Curseur
en mémoire]
B -->|FETCH| C[Traitement
ligne par ligne]
C --> D[Variables]
B -.->|CLOSE| E[Libération]
style B fill:#e1f5ff,stroke:#0066cc,stroke-width:3px
style C fill:#fff4e1,stroke:#ff9800,stroke-width:2px
style E fill:#ffebee,stroke:#c62828,stroke-width:2px
OPEN: exécution de la requête et mise en mémoire
du jeu de résultats.FETCH (ou automatiquement via une boucle
FOR).
CLOSE: libération de la mémoire serveur.| Moment | Action | %FOUND | %NOTFOUND | %ISOPEN |
|---|---|---|---|---|
Après OPEN c; |
Curseur ouvert, avant première ligne | NULL |
NULL |
TRUE |
Après FETCH réussi |
Une ligne a été récupérée | TRUE |
FALSE |
TRUE |
Après FETCH échoué |
Fin du curseur (EOF) | FALSE |
TRUE |
TRUE |
Après CLOSE c; |
Curseur fermé | FALSE |
TRUE |
FALSE |
→ Très utile pour construire des boucles WHILE robustes.
⚠️ Important : %FOUND et %NOTFOUND sont indéfinis (NULL)
après OPEN. Testez-les uniquement APRÈS un FETCH.
SELECT ... INTO ne peut récupérer qu'une seule ligne. Si la requête
renvoie plusieurs lignes, une erreur se produit.ORDER BY ... LIMIT 1 ou un curseur pour parcourir les
résultats.DO $$
DECLARE
c1 CURSOR FOR
SELECT refart FROM article ORDER BY nom;
art article.refart%TYPE;
BEGIN
OPEN c1;
FETCH c1 INTO art; -- affectation de variable
RAISE NOTICE 'Article : %', art;
CLOSE c1;
END;
$$;
Alternative SQL directe:SELECT refart FROM article ORDER BY nom LIMIT 1;
DO $$
DECLARE
c2 CURSOR FOR SELECT refart FROM article ORDER BY nom;
art article.refart%TYPE;
BEGIN
OPEN c2;
FETCH c2 INTO art;
RAISE NOTICE 'Premier article : %', art;
FETCH c2 INTO art;
IF c2%FOUND THEN
RAISE NOTICE 'Deuxième article : %', art;
ELSE
RAISE NOTICE 'Pas de deuxième article';
END IF;
CLOSE c2;
END;
$$;
On ne fait aucune hypothèse sur le nombre d'articles présents dans la table.
Le curseur permet de parcourir dynamiquement toutes les lignes du résultat, quelle que soit
leur quantité.
DO $$
DECLARE
c3 CURSOR FOR
SELECT refart FROM article ORDER BY nom;
art article.refart%TYPE;
BEGIN
OPEN c3;
LOOP
FETCH c3 INTO art;
EXIT WHEN NOT FOUND; -- ou c3%NOTFOUND
RAISE NOTICE 'Article : %', art;
END LOOP;
CLOSE c3;
END;
$$;
LOOP → FETCH → EXIT WHEN NOT FOUND → Traiter
FETCH (pas de duplication)Il existe une autre syntaxe avec WHILE, moins courante :
DO $$
DECLARE
c3 CURSOR FOR SELECT refart FROM article ORDER BY nom;
art article.refart%TYPE;
BEGIN
OPEN c3;
FETCH c3 INTO art; -- Premier FETCH avant la boucle
WHILE c3%FOUND LOOP
RAISE NOTICE 'Article : %', art;
FETCH c3 INTO art; -- FETCH à la fin de la boucle
END LOOP;
CLOSE c3;
END;
$$;
⚠️ Attention : Cette syntaxe nécessite un FETCH avant
et dans la boucle (duplication). Le pattern LOOP + EXIT WHEN
est généralement préféré.
Dans cet exemple, le curseur sélectionne plusieurs colonnes (refart,
prixht) de la table article.
Nous utiliserons une variable de type %ROWTYPE pour stocker une ligne complète.
DO $$
DECLARE
c4 CURSOR FOR
SELECT refart, prixht
FROM article;
art c4%ROWTYPE; -- type dérivé (curseur)
BEGIN
OPEN c4;
LOOP
FETCH c4 INTO art;
EXIT WHEN NOT FOUND;
RAISE NOTICE '% %', art.refart, art.prixht;
END LOOP;
CLOSE c4;
END;
$$;
💡 Avantage de %ROWTYPE :
refart et prixht séparés)
art.refart, art.prixhtSELECT refart, prixht
FROM article
WHERE prixht > (SELECT AVG(prixht) FROM article);
-- ✅ Boucle FOR implicite (recommandée)
FOR x IN
SELECT a, b FROM ma_table
LOOP
RAISE NOTICE '%, %', x.a, x.b;
END LOOP;
-- ⚙️ Équivalent explicite avec FETCH
DECLARE
c CURSOR FOR SELECT a, b FROM ma_table;
x RECORD;
BEGIN
OPEN c;
LOOP
FETCH c INTO x;
EXIT WHEN NOT FOUND;
RAISE NOTICE '%, %', x.a, x.b;
END LOOP;
CLOSE c;
END;
Conclusion: la boucle FOR ... IN SELECT est plus simple et évite
OPEN/FETCH/CLOSE.
| Aspect | Boucle explicite | Boucle implicite |
|---|---|---|
| Syntaxe | OPEN / FETCH / CLOSE | FOR rec IN SELECT ... LOOP |
| Lisibilité | Plus verbeuse | Plus simple et claire |
| Performance | Identique | Identique |
| Usage typique | Traitements complexes ou multi-curseurs | Parcours simple d'un résultat |
→ Toujours privilégier la boucle FOR rec IN SELECT quand c'est possible.
c CURSOR FOR SELECT ...;OPEN c;FETCH c INTO var;CLOSE c;%FOUND, %NOTFOUND, %ISOPENFOR rec IN SELECTLOOP → FETCH → EXIT WHENCLOSE vos curseurs