Oracle Tutorial SQL dinamico PL/SQL: esecuzione immediata e DBMS_SQL
โก Riepilogo intelligente
SQL dinamico in Oracle PL/SQL crea ed esegue istruzioni in fase di runtime, adattando le query ai requisiti in continua evoluzione attraverso due approcci: SQL dinamico nativo con EXECUTE IMMEDIATE e OPEN-FOR, e il pacchetto flessibile DBMS_SQL per i casi complessi.

Cos'รจ l'SQL dinamico?
Dinamico SQL ร una metodologia di programmazione per generare ed eseguire istruzioni in fase di runtime. Viene utilizzata principalmente per scrivere programmi generici e flessibili in cui le istruzioni SQL vengono create ed eseguite in fase di runtime in base alle esigenze, ad esempio quando i nomi delle tabelle, gli elenchi delle colonne o le condizioni WHERE non sono noti fino all'esecuzione del programma.
Modi per scrivere SQL dinamico
PL/SQL offre due modi per scrivere SQL dinamico:
- NDS: SQL dinamico nativo (le istruzioni EXECUTE IMMEDIATE e OPEN-FOR)
- DBMS_SQL (un pacchetto fornito)
La regola generale รจ semplice: se il numero e i tipi di dati delle variabili di input e output sono noti in fase di compilazione, si utilizza SQL dinamico nativo perchรฉ รจ piรน veloce e richiede meno codice. Quando queste informazioni sono note solo in fase di esecuzione, si utilizza il pacchetto DBMS_SQL.
NDS (SQL dinamico nativo): esecuzione immediata
Il SQL dinamico nativo รจ il modo piรน semplice per scrivere SQL dinamico. Utilizza il comando EXECUTE IMMEDIATE per creare ed eseguire il codice SQL in fase di runtime. Per utilizzare questo approccio, รจ necessario conoscere in anticipo il tipo di dati e il numero di variabili utilizzate in fase di runtime. Offre inoltre prestazioni migliori e una minore complessitร rispetto a DBMS_SQL.
Sintassi
EXECUTE IMMEDIATE dynamic_sql_string [INTO {variable[, variable]... | record}] [USING [IN | OUT | IN OUT] bind_argument[, ...]] [RETURNING INTO bind_argument[, ...]];
- stringa sql dinamica: Un'espressione stringa (VARCHAR2 o CHAR, non NVARCHAR2/NCHAR) contenente una singola istruzione SQL o un blocco PL/SQL.
- Clausola INTO: Opzionale. Utilizzato solo quando l'SQL dinamico รจ una SELECT a riga singola; acquisisce i valori restituiti in variabili o in un record. Ogni colonna selezionata necessita di una variabile compatibile con il tipo.
- Clausola USING: Opzionale. Fornisce variabili di collegamento. La modalitร predefinita รจ IN; OUT e IN OUT vengono utilizzati per ricevere valori.
- Clausola di RITORNO: Utilizzato con le istruzioni DML che includono una clausola RETURNING, per acquisire i valori delle righe interessate negli argomenti di bind.
Esempio 1: In questo esempio, recuperiamo i dati dalla tabella emp per emp_no '1001' utilizzando un'istruzione NDS con una variabile di bind.
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; /
Uscita
Employee Name : XXX Employee Number: 1001 Salary : 15000 Manager ID : 1000
Code Spiegazione:
- Righe 2-6: Dichiarazione delle variabili.
- Linea 8: Definizione della query SQL in fase di esecuzione. La query SQL contiene la variabile di bind ':empno' nella clausola WHERE.
- Righe 9-11: Esecuzione della query SQL con EXECUTE IMMEDIATE. Le variabili della clausola INTO contengono i valori recuperati, mentre la clausola USING fornisce il valore per la variabile di bind :empno.
- Righe 12-15: Visualizzazione dei valori recuperati.
Utilizzo di SQL dinamico per DDL
Il PL/SQL statico non puรฒ eseguire direttamente istruzioni DDL come CREATE, ALTER o DROP. EXECUTE IMMEDIATE risolve questo problema costruendo l'istruzione come stringa, il che risulta utile anche quando il nome di un oggetto viene fornito in fase di esecuzione:
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; /
I nomi degli oggetti (tabella, colonna, schema) non possono essere passati come variabili di bind, quindi devono essere concatenati nella stringa. ร sempre necessario convalidare tale input, ad esempio con DBMS_ASSERT.SIMPLE_SQL_NAME, per evitare SQL injection.
DBMS_SQL per SQL dinamico
PL/SQL fornisce il pacchetto DBMS_SQL per lavorare con SQL dinamico quando la struttura dell'istruzione non รจ nota fino al momento dell'esecuzione. Il processo di creazione ed esecuzione di SQL dinamico prevede i seguenti passaggi:
- APRI CURSORE: L'SQL dinamico viene eseguito come un cursorePer eseguire l'istruzione SQL, dobbiamo prima aprire il cursore.
- ANALIZZA SQL: Analizza la query SQL dinamica. Questo verifica la sintassi e la prepara per l'esecuzione.
- VARIABILI DI ASSOCIAZIONE Valori: Assegna i valori alle variabili di bind, se presenti.
- DEFINIRE LA COLONNA: Definisci ciascuna colonna utilizzando la sua posizione relativa nell'istruzione SELECT.
- ESEGUIRE: Eseguire la query analizzata.
- RECUPERA I VALORI: Recupera i valori eseguiti.
- CHIUDI CURSORE: Una volta recuperati i risultati, chiudere il cursore.
Esempio 1: In questo esempio, recuperiamo i dati dalla tabella emp per emp_no '1001' utilizzando un'istruzione DBMS_SQL. Il blocco EXCEPTION chiude il cursore anche in caso di errore.
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; /
Uscita
Employee Name : XXX Employee Number: 1001 Salary : 15000 Manager ID : 1000
Code Spiegazione:
- Righe 1-8: Dichiarazione di variabili.
- Linea 10: Definizione dell'istruzione SQL.
- Linea 11: Apertura del cursore tramite DBMS_SQL.OPEN_CURSOR, che restituisce l'ID del cursore aperto.
- Linea 12: Una volta aperto il cursore, viene analizzato il codice SQL.
- Linea 13: Il valore di binding '1001' viene assegnato al posto di ':empno'.
- Righe 14-17: Definizione delle colonne in base alla loro posizione relativa: (1) emp_name, (2) emp_no, (3) salary, (4) manager.
- Linea 18: Esecuzione della query con DBMS_SQL.EXECUTE, che restituisce il numero di record elaborati.
- Righe 19-32: Recupero dei record in un ciclo. FETCH_ROWS restituisce 0 quando non ci sono piรน righe, interrompendo cosรฌ il ciclo.
- Blocco ECCEZIONE: Garantisce la chiusura del cursore, impedendo cosรฌ che i cursori aperti perdano memoria in caso di errore.
NDS vs DBMS_SQL: quando usare l'uno o l'altro
Entrambi gli approcci eseguono query SQL in fase di runtime, ma sono adatti a situazioni diverse:
- Utilizzare SQL dinamico nativo (EXECUTE IMMEDIATE / OPEN-FOR) quando il numero e i tipi di dati degli input e degli output sono noti in fase di compilazione. ร piรน veloce, piรน facile da leggere e richiede meno codice.
- Utilizzare DBMS_SQL quando la struttura รจ sconosciuta fino al momento dell'esecuzione, ad esempio una query il cui numero di colonne selezionate o variabili di binding varia, nota come SQL dinamico di metodo 4, oppure un'istruzione troppo grande per essere contenuta in una singola variabile VARCHAR2 da 32K.


