
Le SQL dynamique permet de construire et d’exécuter des requêtes dont le texte n’est connu qu’à l’exécution. En PL/SQL, on l’utilise via EXECUTE IMMEDIATE (et DBMS_SQL pour les cas avancés). Indispensable pour le DDL, les noms de tables variables ou les filtres dynamiques — mais à manier avec rigueur pour éviter l’injection SQL.
🧪 Envie de pratiquer ? Exécutez les exemples de cet article dans notre SQL Playground gratuit — aucun logiciel à installer.
Pourquoi le SQL dynamique ?
Le PL/SQL « statique » ne permet pas certaines choses :
- exécuter du DDL (
CREATE,ALTER,TRUNCATE) dans une procédure ; - changer le nom de la table/colonne au moment de l’exécution ;
- construire un WHERE selon des critères optionnels.
EXECUTE IMMEDIATE : la base
BEGIN
-- DDL (impossible en PL/SQL statique)
EXECUTE IMMEDIATE 'TRUNCATE TABLE logs_temp';
END;Récupérer une valeur : INTO
DECLARE
v_nb NUMBER;
v_table VARCHAR2(30) := 'CLIENTS';
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || v_table INTO v_nb;
DBMS_OUTPUT.put_line(v_nb);
END;Passer des paramètres : USING (à TOUJOURS privilégier)
DECLARE
v_nom clients.nom%TYPE;
BEGIN
EXECUTE IMMEDIATE
'SELECT nom FROM clients WHERE id = :1'
INTO v_nom
USING p_id; -- bind variable, pas de concaténation
END;Les bind variables (:1 + USING) protègent contre l’injection SQL et améliorent les performances (réutilisation des plans).
⚠️ Sécurité : éviter l’injection SQL
-- ❌ DANGER : concaténer une valeur utilisateur
EXECUTE IMMEDIATE 'SELECT * FROM clients WHERE nom = ''' || p_nom || '''';
-- ✅ paramètre lié
EXECUTE IMMEDIATE 'SELECT * FROM clients WHERE nom = :1' USING p_nom;Règle : les valeurs passent par USING. Seuls les identifiants (nom de table/colonne) se concatènent — et uniquement après validation (liste blanche, DBMS_ASSERT.SQL_OBJECT_NAME).
v_table := DBMS_ASSERT.SQL_OBJECT_NAME(v_table); -- valide le nom d'objetDML dynamique et nombre de lignes
DECLARE
v_maj NUMBER;
BEGIN
EXECUTE IMMEDIATE
'UPDATE comptes SET solde = solde * :taux WHERE actif = :a'
USING 1.01, 'O';
v_maj := SQL%ROWCOUNT;
DBMS_OUTPUT.put_line(v_maj || ' comptes mis à jour');
END;WHERE dynamique (filtres optionnels)
DECLARE
v_sql VARCHAR2(4000) := 'SELECT * FROM clients WHERE 1=1';
v_cur SYS_REFCURSOR;
BEGIN
IF p_ville IS NOT NULL THEN
v_sql := v_sql || ' AND ville = :v';
END IF;
-- ... ouverture avec les bons USING selon les filtres
END;Pour des cas très dynamiques (nombre de colonnes inconnu), passez à DBMS_SQL, plus verbeux mais totalement flexible.
EXECUTE IMMEDIATE vs DBMS_SQL
- EXECUTE IMMEDIATE : simple, rapide, couvre 90% des besoins (structure connue à l’écriture).
- DBMS_SQL : pour le « method 4 » — nombre de colonnes/binds inconnu à l’avance.
Bonnes pratiques
- Valeurs →
USING; jamais de concaténation de données utilisateur. - Identifiants concaténés → valider avec
DBMS_ASSERT. - Le SQL dynamique est plus difficile à maintenir : ne l’utilisez que lorsque le statique ne suffit pas.
FAQ
Pourquoi utiliser EXECUTE IMMEDIATE plutôt qu’une requête normale ?
Uniquement quand le texte SQL n’est pas connu à l’écriture : DDL, nom d’objet variable, filtres optionnels. Sinon, le SQL statique est plus sûr et plus lisible.
EXECUTE IMMEDIATE protège-t-il de l’injection SQL ?
Seulement si vous passez les valeurs par USING (bind variables). La concaténation de données utilisateur reste vulnérable.
Peut-on faire un COMMIT dans du SQL dynamique ?
Oui, mais le DDL (CREATE/ALTER/TRUNCATE) effectue de toute façon un COMMIT implicite — attention aux transactions en cours.
