Oracle Tutoriel PL/SQL Dynamic SQL : Exécution immédiate et DBMS_SQL
⚡ Résumé intelligent
SQL dynamique dans Oracle PL/SQL construit et exécute des instructions au moment de l'exécution, adaptant les requêtes aux exigences changeantes grâce à deux approches : le SQL dynamique natif avec EXECUTE IMMEDIATE et OPEN-FOR, et le package flexible DBMS_SQL pour les cas complexes.

Qu’est-ce que le SQL dynamique ?
Dynamique SQL SQL est une méthodologie de programmation permettant de générer et d'exécuter des instructions SQL à l'exécution. Elle est principalement utilisée pour écrire des programmes génériques et flexibles où les instructions SQL sont créées et exécutées à l'exécution en fonction des besoins, par exemple lorsque les noms de tables, les listes de colonnes ou les conditions WHERE ne sont connus qu'au moment de l'exécution du programme.
Méthodes d'écriture de SQL dynamique
PL/SQL offre deux façons d'écrire du SQL dynamique :
- NDS – SQL dynamique natif (les instructions EXECUTE IMMEDIATE et OPEN-FOR)
- DBMS_SQL (un colis fourni)
La règle générale est simple : si le nombre et les types de données des variables d’entrée et de sortie sont connus à la compilation, utilisez le SQL dynamique natif, car il est plus rapide et nécessite moins de code. Si ces informations ne sont connues qu’à l’exécution, utilisez le package DBMS_SQL.
NDS (Native Dynamic SQL) – Exécution immédiate
Le SQL dynamique natif est la méthode la plus simple pour écrire du SQL dynamique. Il utilise la commande EXECUTE IMMEDIATE pour créer et exécuter le code SQL à l'exécution. Pour utiliser cette approche, le type de données et le nombre de variables utilisées à l'exécution doivent être connus à l'avance. Il offre également de meilleures performances et une complexité moindre comparé à DBMS_SQL.
Syntaxe
EXECUTE IMMEDIATE dynamic_sql_string [INTO {variable[, variable]... | record}] [USING [IN | OUT | IN OUT] bind_argument[, ...]] [RETURNING INTO bind_argument[, ...]];
- chaîne_sql_dynamique : Une expression de chaîne (VARCHAR2 ou CHAR, et non NVARCHAR2/NCHAR) contenant une seule instruction SQL ou un seul bloc PL/SQL.
- clause INTO : Facultatif. Utilisé uniquement lorsque la requête SQL dynamique est une requête SELECT sur une seule ligne ; elle stocke les valeurs renvoyées dans des variables ou un enregistrement. Chaque colonne sélectionnée doit avoir une variable de type compatible.
- clause USING : Optionnel. Permet de lier des variables. Le mode par défaut est IN ; les modes OUT et IN OUT servent à recevoir des valeurs en retour.
- RETOUR À LA clause : Utilisé avec les instructions DML comportant une clause RETURNING, pour capturer les valeurs des lignes affectées dans les arguments de liaison.
Exemple 1: Dans cet exemple, nous récupérons les données de la table emp pour emp_no '1001' à l'aide d'une instruction NDS avec une variable de liaison.
DECLARE lv_sql VARCHAR2(500); lv_emp_name VARCHAR2(50); ln_emp_no NUMBER; ln_salary NUMBER; ln_manager NUMBER; BEGIN lv_sql := 'SELECT emp_name, emp_no, salary, manager FROM emp WHERE emp_no = :empno'; EXECUTE IMMEDIATE lv_sql INTO lv_emp_name, ln_emp_no, ln_salary, ln_manager USING 1001; DBMS_OUTPUT.PUT_LINE('Employee Name: ' || lv_emp_name); DBMS_OUTPUT.PUT_LINE('Employee Number: ' || ln_emp_no); DBMS_OUTPUT.PUT_LINE('Salary: ' || ln_salary); DBMS_OUTPUT.PUT_LINE('Manager ID: ' || ln_manager); END; /
Sortie
Employee Name : XXX Employee Number: 1001 Salary : 15000 Manager ID : 1000
Code Explication:
- Lignes 2-6 : Déclaration des variables.
- Ligne 8: Encadrement de la requête SQL à l'exécution. La requête SQL contient la variable de liaison ':empno' dans la clause WHERE.
- Lignes 9-11 : Exécution de la requête SQL encadrée avec EXECUTE IMMEDIATE. Les variables de la clause INTO contiennent les valeurs extraites, et la clause USING fournit la valeur de la variable liée :empno.
- Lignes 12-15 : Affichage des valeurs récupérées.
Utilisation du SQL dynamique pour le DDL
Le PL/SQL statique ne peut pas exécuter directement des instructions DDL telles que CREATE, ALTER ou DROP. L'instruction EXECUTE IMMEDIATE résout ce problème en construisant l'instruction sous forme de chaîne de caractères, ce qui est également pratique lorsqu'un nom d'objet est fourni lors de l'exécution.
DECLARE l_table_name VARCHAR2(30) := 'my_table'; l_sql_stmt VARCHAR2(200); BEGIN l_sql_stmt := 'CREATE TABLE ' || l_table_name || ' (id NUMBER, name VARCHAR2(30))'; EXECUTE IMMEDIATE l_sql_stmt; END; /
Les noms d'objets (table, colonne, schéma) ne peuvent pas être transmis comme variables liées ; ils doivent donc être concaténés dans la chaîne. Il est impératif de toujours valider ces données, par exemple avec DBMS_ASSERT.SIMPLE_SQL_NAME, afin d'éviter les injections SQL.
DBMS_SQL pour SQL dynamique
PL/SQL fournit le package DBMS_SQL pour la manipulation de requêtes SQL dynamiques lorsque la structure de l'instruction n'est connue qu'à l'exécution. Le processus de création et d'exécution de ces requêtes SQL dynamiques comprend les étapes suivantes :
- OUVRIR LE CURSEUR : Le SQL dynamique s'exécute comme un curseurPour exécuter l'instruction SQL, nous devons d'abord ouvrir le curseur.
- ANALYSER SQL : Analyser le SQL dynamique. Cela vérifie la syntaxe et prépare la requête pour l'exécution.
- Valeurs de la variable BIND : Attribuez les valeurs aux variables liées, le cas échéant.
- DÉFINIR LA COLONNE : Définissez chaque colonne en utilisant sa position relative dans l'instruction SELECT.
- EXÉCUTER: Exécutez la requête analysée.
- VALEURS DE RÉCUPÉRATION : Récupérez les valeurs exécutées.
- FERMER LE CURSEUR : Une fois les résultats récupérés, fermez le curseur.
Exemple 1: Dans cet exemple, nous récupérons les données de la table « emp » pour l'employé n° 1001 à l'aide d'une instruction DBMS_SQL. Le bloc EXCEPTION ferme le curseur même en cas d'erreur.
DECLARE lv_sql VARCHAR2(500); lv_emp_name VARCHAR2(50); ln_emp_no NUMBER; ln_salary NUMBER; ln_manager NUMBER; ln_cursor_id NUMBER; ln_rows_processed NUMBER; BEGIN lv_sql := 'SELECT emp_name, emp_no, salary, manager FROM emp WHERE emp_no = :empno'; ln_cursor_id := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE(ln_cursor_id, lv_sql, DBMS_SQL.NATIVE); DBMS_SQL.BIND_VARIABLE(ln_cursor_id, ':empno', 1001); DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 1, lv_emp_name, 50); DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 2, ln_emp_no); DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 3, ln_salary); DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 4, ln_manager); ln_rows_processed := DBMS_SQL.EXECUTE(ln_cursor_id); LOOP IF DBMS_SQL.FETCH_ROWS(ln_cursor_id) = 0 THEN EXIT; ELSE DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 1, lv_emp_name); DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 2, ln_emp_no); DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 3, ln_salary); DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 4, ln_manager); DBMS_OUTPUT.PUT_LINE('Employee Name: ' || lv_emp_name); DBMS_OUTPUT.PUT_LINE('Employee Number: ' || ln_emp_no); DBMS_OUTPUT.PUT_LINE('Salary: ' || ln_salary); DBMS_OUTPUT.PUT_LINE('Manager ID: ' || ln_manager); END IF; END LOOP; DBMS_SQL.CLOSE_CURSOR(ln_cursor_id); EXCEPTION WHEN OTHERS THEN DBMS_SQL.CLOSE_CURSOR(ln_cursor_id); END; /
Sortie
Employee Name : XXX Employee Number: 1001 Salary : 15000 Manager ID : 1000
Code Explication:
- Lignes 1-8 : Déclaration de variable.
- Ligne 10: Formulation de la requête SQL.
- Ligne 11: Ouverture du curseur à l'aide de DBMS_SQL.OPEN_CURSOR, qui renvoie l'identifiant du curseur ouvert.
- Ligne 12: Une fois le curseur ouvert, le code SQL est analysé.
- Ligne 13: La valeur de liaison '1001' est attribuée à la place de ':empno'.
- Lignes 14-17 : Définition des colonnes par leur position relative : (1) emp_name, (2) emp_no, (3) salary, (4) manager.
- Ligne 18: L'exécution de la requête avec DBMS_SQL.EXECUTE renvoie le nombre d'enregistrements traités.
- Lignes 19-32 : Récupération des enregistrements en boucle. La fonction FETCH_ROWS renvoie 0 lorsqu'il ne reste plus de lignes, ce qui met fin à la boucle.
- bloc EXCEPTION : Garantit la fermeture du curseur afin d'éviter les fuites de mémoire en cas d'erreur.
NDS ou DBMS_SQL : quand utiliser lequel ?
Les deux approches exécutent des requêtes SQL à l'exécution, mais elles conviennent à des situations différentes :
- Utiliser SQL dynamique natif (EXECUTE IMMEDIATE / OPEN-FOR) Lorsque le nombre et les types de données des entrées et des sorties sont connus à la compilation, le code est plus rapide, plus lisible et nécessite moins de code.
- Utilisez DBMS_SQL lorsque la structure est inconnue jusqu'à l'exécution, par exemple une requête dont le nombre de colonnes sélectionnées ou de variables liées varie, connue sous le nom de SQL dynamique de méthode 4, ou une instruction trop grande pour tenir dans une seule variable VARCHAR2 de 32 Ko.


