Oracle Tutorial PL/SQL Dynamic SQL: Executați imediat și DBMS_SQL

⚡ Rezumat inteligent

SQL dinamic în Oracle PL/SQL construiește și execută instrucțiuni în timpul execuției, adaptând interogările la cerințele în schimbare prin două abordări: SQL dinamic nativ cu EXECUTE IMMEDIATE și OPEN-FOR și pachetul flexibil DBMS_SQL pentru cazuri complexe.

  • ⚙️ SQL în timpul execuției: SQL dinamic generează și execută instrucțiuni atunci când numele tabelelor sau coloanelor sunt necunoscute în prealabil.
  • SQL dinamic nativ: EXECUTE IMMEDIATE creează și execută SQL rapid cu cel mai puțin cod.
  • 🔁 DESCHIS PENTRU: Gestionează interogările dinamice pe mai multe rânduri pe care EXECUTE IMMEDIATE nu le poate prelua singură.
  • 🧩 SGBD_SQL: Se potrivește instrucțiunilor al căror număr de coloane sau tipuri sunt necunoscute până la momentul execuției.
  • 🔐 Variabile de legare: Clauza USING transmite valori pozițional și blochează injecția SQL.
  • 🤖 Asistență AI: Instrumentele de inteligență artificială elaborează SQL dinamic și semnalează riscurile de injectare în timpul revizuirii.

Oracle Tutorial PL/SQL Dynamic SQL

Ce este SQL dinamic?

Dinamic SQL este o metodologie de programare pentru generarea și rularea instrucțiunilor la momentul execuției. Este utilizată în principal pentru a scrie programe flexibile și de uz general, în care instrucțiunile SQL sunt create și executate la momentul execuției pe baza cerințelor, de exemplu, atunci când numele tabelelor, listele de coloane sau condițiile WHERE nu sunt cunoscute până la rularea programului.

Modalități de a scrie SQL dinamic

PL/SQL oferă două modalități de scriere SQL dinamic:

  1. NDS – SQL dinamic nativ (instrucțiunile EXECUTE IMMEDIATE și OPEN-FOR)
  2. DBMS_SQL (un pachet furnizat)

Regula generală este simplă: dacă numărul și tipurile de date ale variabilelor de intrare și ieșire sunt cunoscute la momentul compilării, utilizați SQL dinamic nativ deoarece este mai rapid și necesită mai puțin cod. Când aceste informații sunt cunoscute doar la momentul execuției, utilizați pachetul DBMS_SQL.

NDS (Native Dynamic SQL) – Executare imediată

SQL dinamic nativ este cea mai ușoară modalitate de a scrie SQL dinamic. Folosește comanda EXECUTE IMMEDIATE pentru a crea și executa codul SQL la momentul execuției. Pentru a utiliza această abordare, tipul de date și numărul de variabile utilizate la momentul execuției trebuie cunoscute în prealabil. De asemenea, oferă performanțe mai bune și o complexitate mai mică în comparație cu DBMS_SQL.

Sintaxă

EXECUTE IMMEDIATE dynamic_sql_string
[INTO {variable[, variable]... | record}]
[USING [IN | OUT | IN OUT] bind_argument[, ...]]
[RETURNING INTO bind_argument[, ...]];
  • șir_sql_dinamic: O expresie de tip șir de caractere (VARCHAR2 sau CHAR, nu NVARCHAR2/NCHAR) care conține o singură instrucțiune SQL sau un bloc PL/SQL.
  • Clauza INTO: Opțional. Se utilizează numai când SQL-ul dinamic este un SELECT pe un singur rând; capturează valorile returnate în variabile sau într-o înregistrare. Fiecare coloană selectată necesită o variabilă compatibilă cu tipul.
  • Clauza USING: Opțional. Furnizează variabile de legătură. Modul implicit este IN; OUT și IN OUT sunt folosite pentru a primi valori înapoi.
  • Clauza RETURNING INTO: Se utilizează cu instrucțiuni DML care conțin o clauză RETURNING, pentru a captura valorile rândurilor afectate în argumente de legare.

Exemplu 1: În acest exemplu, preluăm datele din tabela emp pentru emp_no '1001' folosind o instrucțiune NDS cu o variabilă bind.

NDS - Executare imediată

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;
/

producție

Employee Name  : XXX
Employee Number: 1001
Salary         : 15000
Manager ID     : 1000

Code Explicaţie:

  • Liniile 2-6: Declararea variabilelor.
  • Linia 8: Încadrarea comenzii SQL la momentul execuției. Comanda SQL conține variabila de legătură ':empno' în condiția WHERE.
  • Liniile 9-11: Executarea comenzii SQL încadrată cu EXECUTE IMMEDIATE. Variabilele clauzei INTO conțin valorile extrase, iar clauza USING furnizează valoarea pentru variabila de legătură :empno.
  • Liniile 12-15: Afișarea valorilor obținute.

Utilizarea SQL dinamic pentru DDL

PL/SQL static nu poate rula DDL precum CREATE, ALTER sau DROP direct. EXECUTE IMMEDIATE rezolvă această problemă prin construirea instrucțiunii ca șir de caractere, ceea ce este util și atunci când un nume de obiect este furnizat la momentul execuției:

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;
/

Numele obiectelor (tabel, coloană, schemă) nu pot fi transmise ca variabile de legătură, deci trebuie concatenate în șir. Validați întotdeauna astfel de intrări, de exemplu cu DBMS_ASSERT.SIMPLE_SQL_NAME, pentru a evita injecția SQL.

DBMS_SQL pentru SQL dinamic

PL/SQL oferă pachetul DBMS_SQL pentru lucrul cu SQL dinamic atunci când structura instrucțiunii nu este cunoscută până la momentul execuției. Procesul de creare și executare a SQL-ului dinamic implică următorii pași:

  • DESCHIDE CURSORUL: SQL-ul dinamic se execută ca un cursorPentru a executa instrucțiunea SQL, trebuie mai întâi să deschidem cursorul.
  • PARSE SQL: Analizează codul SQL dinamic. Aceasta verifică sintaxa și menține interogarea gata de execuție.
  • Valori VARIABILE BIND: Atribuiți valorile pentru variabilele de legare, dacă există.
  • DEFINIRE COLOANĂ: Definiți fiecare coloană folosind poziția sa relativă în instrucțiunea select.
  • A EXECUTA: Executați interogarea analizată.
  • VALORI DE PRELUARE: Preia valorile executate.
  • ÎNCHIDE CURSOR: După ce rezultatele sunt obținute, închideți cursorul.

Exemplu 1: În acest exemplu, preluăm datele din tabela emp pentru emp_no '1001' folosind o instrucțiune DBMS_SQL. Blocul EXCEPTION închide cursorul chiar dacă apare o eroare.

DBMS_SQL pentru SQL dinamic

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;
/

producție

Employee Name  : XXX
Employee Number: 1001
Salary         : 15000
Manager ID     : 1000

Code Explicaţie:

  • Liniile 1-8: Declararea variabilelor.
  • Linia 10: Încadrarea instrucțiunii SQL.
  • Linia 11: Deschiderea cursorului folosind DBMS_SQL.OPEN_CURSOR, care returnează id-ul cursorului deschis.
  • Linia 12: După ce cursorul este deschis, codul SQL este analizat.
  • Linia 13: Valoarea de legare „1001” este atribuită în locul lui „:empno”.
  • Liniile 14-17: Definirea coloanelor după poziția lor relativă: (1) nume_angajat, (2) număr_angajat, (3) salariu, (4) manager.
  • Linia 18: Executarea interogării cu DBMS_SQL.EXECUTE, care returnează numărul de înregistrări procesate.
  • Liniile 19-32: Preluarea înregistrărilor într-o buclă. FETCH_ROWS returnează 0 când nu mai rămân rânduri, ceea ce iese din buclă.
  • Bloc EXCEPȚII: Asigură că cursorul este închis, astfel încât cursoarele deschise să nu aibă scurgeri dacă este generată o eroare.

NDS vs DBMS_SQL: Când să folosiți care

Ambele abordări execută SQL în timpul execuției, dar se potrivesc situațiilor diferite:

  • Folosește SQL dinamic nativ (EXECUTE IMMEDIATE / OPEN-FOR) când numărul și tipurile de date ale intrărilor și ieșirilor sunt cunoscute la momentul compilării. Este mai rapid, mai ușor de citit și necesită mai puțin cod.
  • Utilizați DBMS_SQL când structura este necunoscută până la momentul execuției, de exemplu o interogare al cărei număr de coloane selectate sau variabile de legătură variază, cunoscută sub numele de SQL dinamic metoda 4, sau o instrucțiune prea mare pentru a încăpea într-o singură variabilă VARCHAR2 de 32K.

Întrebări frecvente

Variabilele de legare transmit datele introduse de utilizator ca date, niciodată ca și cod executabil. Clauza USING furnizează valori pozițional, astfel încât textul malițios nu poate modifica structura instrucțiunilor. Legați întotdeauna datele introduse nesigure în loc să le concatenați.

Nu. Oracle leagă doar valorile datelor, nu și numele obiectelor. Concatenează identificatorii în șirul SQL și validează-i cu DBMS_ASSERT.SIMPLE_SQL_NAME pentru a te proteja de injectare.

EXECUTE IMMEDIATE preia doar un rând. Pentru mai multe rânduri, deschideți o instrucțiune REF CURSOR cu instrucțiunea OPEN-FOR, apoi parcurgeți FETCH până la %NOTFOUND și CLOSE cursorul.

Adăugați o clauză RETURNING la INSERT, UPDATE sau DELETE, apoi utilizați clauza RETURNING INTO din EXECUTE IMMEDIATE pentru a captura valorile rândului afectat în argumentele de legare.

SQL dinamic adaugă costuri suplimentare de analiză deoarece instrucțiunile se compilează la momentul execuției. Reutilizarea variabilelor de legare permite Oracle partajează cursoarele și reduce analizele dure, keeping performanță apropiată de SQL static.

Șirul trebuie să fie VARCHAR2 sau CHAR. Tipurile de caractere naționale precum NVARCHAR2 și NCHAR nu sunt permise. Pentru text peste 32K, DBMS_SQL acceptă o colecție de elemente VARCHAR2.

Da. Asistenții AI, cum ar fi GitHub Copilot, creează blocuri EXECUTE IMMEDIATE și DBMS_SQL din prompturi simple, sugerează substituenți pentru variabile de legare și explică fiecare clauză, deși un dezvoltator ar trebui să revizuiască în continuare rezultatul.

Scanerele de cod bazate pe inteligență artificială semnalează intrările concatenate ale utilizatorilor și recomandă variabile de legare sau verificări DBMS_ASSERT. Acestea evidențiază modele riscante în timpul revizuirii, ajutândping Echipele identifică defectele de injecție înainte de implementare.

Rezumați această postare cu: