Oracle PL/SQL-i dünaamiline SQL-i õpetus: käivitage kohe ja DBMS_SQL

⚡ Nutikas kokkuvõte

Dünaamiline SQL Oracle PL/SQL loob ja käivitab lauseid käitusajal, kohandades päringuid muutuvatele nõuetele kahe lähenemisviisi abil: natiivne dünaamiline SQL koos EXECUTE IMMEDIATE ja OPEN-FOR funktsioonidega ning paindlik DBMS_SQL pakett keerukate juhtumite jaoks.

  • ⚙️ Käitusaegne SQL: Dünaamiline SQL genereerib ja käivitab lauseid, kui tabeli või veeru nimed pole eelnevalt teada.
  • Natiivne dünaamiline SQL: EXECUTE IMMEDIATE loob ja käivitab SQL-i kiiresti minimaalse koodikogusega.
  • 🔁 AVATUD: Käsitleb mitmerealisi dünaamilisi päringuid, mida EXECUTE IMMEDIATE üksi ei suuda hankida.
  • 🧩 DBMS_SQL: Sobib lausetele, mille veergude arv või tüübid on enne käitusaega teadmata.
  • 🔐 Sidumismuutujad: USING-klausel edastab väärtused positsiooniliselt ja blokeerib SQL-süstimise.
  • 🤖 AI abi: Tehisintellekti tööriistad kavandavad dünaamilisi SQL-e ja märgistavad süstimise riske läbivaatamise ajal.

Oracle PL/SQL dünaamiline SQL-i õpetus

Mis on dünaamiline SQL?

Dünaamiline SQL on programmeerimismetoodika lausete genereerimiseks ja käivitamiseks käitusajal. Seda kasutatakse peamiselt üldotstarbeliste ja paindlike programmide kirjutamiseks, kus SQL-laused luuakse ja käivitatakse käitusajal vastavalt vajadusele, näiteks kui tabeli nimed, veergude loendid või WHERE-tingimused pole enne programmi käivitamist teada.

Dünaamilise SQL-i kirjutamise viisid

PL/SQL pakub dünaamilise SQL-i kirjutamiseks kahte võimalust:

  1. NDS – Native Dynamic SQL (laused EXECUTE IMMEDIATE ja OPEN-FOR)
  2. DBMS_SQL (kaasasolev pakett)

Üldreegel on lihtne: kui sisend- ja väljundmuutujate arv ja andmetüübid on kompileerimise ajal teada, kasutage Native Dynamic SQL-i, kuna see on kiirem ja nõuab vähem koodi. Kui see teave on teada ainult käitusajal, kasutage DBMS_SQL-i paketti.

NDS (native Dynamic SQL) – käivitage kohe

Natiivne dünaamiline SQL on dünaamilise SQL-i kirjutamise lihtsam viis. See kasutab SQL-i loomiseks ja käivitamiseks käitusajal käsku EXECUTE IMMEDIATE. Selle lähenemisviisi kasutamiseks peavad käitusajal kasutatavate muutujate andmetüüp ja arv olema eelnevalt teada. See annab ka parema jõudluse ja väiksema keerukuse võrreldes DBMS_SQL-iga.

Süntaks

EXECUTE IMMEDIATE dynamic_sql_string
[INTO {variable[, variable]... | record}]
[USING [IN | OUT | IN OUT] bind_argument[, ...]]
[RETURNING INTO bind_argument[, ...]];
  • dünaamiline_sql_string: Stringavaldis (VARCHAR2 või CHAR, mitte NVARCHAR2/NCHAR), mis sisaldab ühte SQL-lauset või PL/SQL-plokki.
  • INTO-klausel: Valikuline. Kasutatakse ainult siis, kui dünaamiline SQL on üherealine SELECT; see jäädvustab tagastatud väärtused muutujatesse või kirjesse. Iga valitud veerg vajab tüübiga ühilduvat muutujat.
  • KASUTUSklausel: Valikuline. Annab siduvad muutujad. Vaikimisi režiim on IN; OUT ja IN OUT kasutatakse väärtuste vastuvõtmiseks.
  • NAASUMISE klausel: Kasutatakse DML-lausetega, mis sisaldavad RETURNING-klauslit, et jäädvustada mõjutatud rea väärtused sidumisargumentidesse.

Näide 1: Selles näites toome andmed emp_no '1001' jaoks emp-tabelist, kasutades NDS-lauset sidumismuutujaga.

NDS – Käivitage kohe

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

Väljund

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

Code Selgitus:

  • Read 2–6: Muutujate deklareerimine.
  • Rida 8: SQL-i raamimine käitusajal. SQL sisaldab WHERE-tingimuses sidumismuutujat ':empno'.
  • Read 9–11: Raamitud SQL-i käivitamine käsuga EXECUTE IMMEDIATE. INTO-klausli muutujad hoiavad allalaaditud väärtusi ja USING-klausel annab väärtuse sidumismuutujale :empno.
  • Read 12–15: Kuvatakse hangitud väärtusi.

Dünaamilise SQL-i kasutamine DDL-i jaoks

Staatiline PL/SQL ei saa otse käivitada DDL-käske, näiteks CREATE, ALTER või DROP. EXECUTE IMMEDIATE lahendab selle probleemi, luues lause stringina, mis on mugav ka siis, kui objekti nimi antakse käitusajal:

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

Objektinimesid (tabel, veerg, skeem) ei saa sidumismuutujatena edastada, seega tuleb need stringiks liita. SQL-süstimise vältimiseks valideerige selline sisend alati, näiteks DBMS_ASSERT.SIMPLE_SQL_NAME abil.

DBMS_SQL dünaamilise SQL-i jaoks

PL/SQL pakub DBMS_SQL paketti dünaamilise SQL-iga töötamiseks, kui lause struktuur pole enne käitusaega teada. Dünaamilise SQL-i loomise ja käivitamise protsess hõlmab järgmisi samme:

  • AVA KURSORI: Dünaamiline SQL käivitub nagu kursorSQL-lause täitmiseks peame kõigepealt kursori avama.
  • PARSEERI SQL: Parsi dünaamilist SQL-i. See kontrollib süntaksit ja hoiab päringu käivitamiseks valmis.
  • BIND MUUTUJA Väärtused: Määrake sidumismuutujate väärtused, kui neid on.
  • MÄÄRA VEERG: Defineeri iga veerg selle suhtelise positsiooni abil valikulauses.
  • TÄITMINE: Käivita parsitud päring.
  • VÄÄRTUSTE TOOMINE: Tooge täidetud väärtused.
  • SULGE KURSORI: Kui tulemused on laekunud, sulgege kursor.

Näide 1: Selles näites toome andmed emp_no '1001' jaoks emp-tabelist, kasutades DBMS_SQL-lauset. EXCEPTION-plokk sulgeb kursori isegi vea ilmnemisel.

DBMS_SQL dünaamilise SQL-i jaoks

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

Väljund

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

Code Selgitus:

  • Read 1–8: Muutuja deklaratsioon.
  • Rida 10: SQL-lause raamimine.
  • Rida 11: Kursori avamine DBMS_SQL.OPEN_CURSOR abil, mis tagastab avatud kursori ID.
  • Rida 12: Pärast kursori avamist parsitakse SQL.
  • Rida 13: ':empno' asemele määratakse sidumisväärtus '1001'.
  • Read 14–17: Veergude defineerimine nende suhtelise positsiooni järgi: (1) töötaja_nimi, (2) töötaja_number, (3) palk, (4) juht.
  • Rida 18: Päringu täitmine funktsiooniga DBMS_SQL.EXECUTE, mis tagastab töödeldud kirjete arvu.
  • Read 19–32: Kirjete toomine tsüklis. FETCH_ROWS tagastab 0, kui ridu pole enam alles, mis väljub tsüklist.
  • ERANDITE plokk: Tagab kursori sulgemise, et avatud kursorid vea tekkimisel ei lekiks.

NDS vs DBMS_SQL: millal millist kasutada

Mõlemad lähenemisviisid käitavad SQL-i käitusajal, kuid sobivad erinevatele olukordadele:

  • Kasuta natiivset dünaamilist SQL-i (TÄIDA KOHE / AVA FOR) kui sisendite ja väljundite arv ja andmetüübid on kompileerimise ajal teada. See on kiirem, hõlpsamini loetav ja nõuab vähem koodi.
  • Kasuta DBMS_SQL-i kui struktuur on käitusajani tundmatu, näiteks päringu puhul, mille valitud veergude või sidumismuutujate arv varieerub, mida tuntakse meetodi 4 dünaamilise SQL-ina või lause puhul, mis on liiga suur, et mahtuda ühte 32K VARCHAR2 muutujasse.

KKK

Sidumismuutujad edastavad kasutaja sisendi andmetena, mitte kunagi käivitatava koodina. USING-klausel annab väärtused positsiooniliselt, seega pahatahtlik tekst ei saa lause struktuuri muuta. Ebausaldusväärse sisendi seotakse alati selle liitmise asemel.

Ei. Oracle Seob ainult andmeväärtusi, mitte objektinimesid. Ühenda identifikaatorid SQL-stringiks ja valideeri need DBMS_ASSERT.SIMPLE_SQL_NAME abil, et vältida süstimist.

EXECUTE IMMEDIATE toob välja ainult ühe rea. Paljude ridade korral avage REF CURSOR OPEN-FOR-lausega, seejärel tehke FETCH-tsükkel kuni %NOTFOUND-ni ja SULGEGE kursor.

Lisage INSERT, UPDATE või DELETE käsule RETURNING-klausel ja seejärel kasutage EXECUTE IMMEDIATE käsu RETURNING INTO-klauslit, et jäädvustada mõjutatud rea väärtused sidumisargumentideks.

Dünaamiline SQL lisab parsimiskoormust, kuna laused kompileeritakse käitusajal. Sidumismuutujate taaskasutamine võimaldab Oracle jaga kursoreid ja vähenda kõvasid parse, keeping jõudlus on lähedane staatilisele SQL-ile.

String peab olema VARCHAR2 või CHAR. Riiklikud märgitüübid, näiteks NVARCHAR2 ja NCHAR, ei ole lubatud. Üle 32 kB suuruse teksti puhul aktsepteerib DBMS_SQL VARCHAR2 elementide kogumit.

Jah. Tehisintellekti assistendid, näiteks GitHub Copilot, joonistavad EXECUTE IMMEDIATE ja DBMS_SQL plokid tavalistest viipadest, pakuvad välja sidumismuutujate kohahoidjaid ja selgitavad iga klauslit, kuigi arendaja peaks väljundit ikkagi üle vaatama.

Tehisintellektil põhinevad koodiskannerid märgistavad liidetud kasutaja sisendit ja soovitavad sidumismuutujaid või DBMS_ASSERT kontrolle. Need toovad läbivaatamise ajal esile riskantsed mustrid, aitavad...ping meeskonnad avastavad süstimisvead enne juurutamist.

Võta see postitus kokku järgmiselt: