Oracle PL/SQL Dynamic SQL Tutorial: Suorita välitön & DBMS_SQL

⚡ Älykäs yhteenveto

Dynaaminen SQL sisään Oracle PL/SQL muodostaa ja suorittaa lauseita suorituksen aikana mukauttaen kyselyitä muuttuviin vaatimuksiin kahdella lähestymistavalla: natiivilla dynaamisella SQL:llä, jossa on EXECUTE IMMEDIATE- ja OPEN-FOR-käskyt, sekä joustavalla DBMS_SQL-paketilla monimutkaisia ​​tapauksia varten.

  • ⚙️ Ajonaikainen SQL: Dynaaminen SQL luo ja suorittaa lauseita, kun taulukon tai sarakkeen nimet ovat etukäteen tuntemattomia.
  • Natiivi dynaaminen SQL: SUORITA HETI luo ja suorittaa SQL:n nopeasti vähimmällä koodilla.
  • 🔁 AVOINNA: Käsittelee monirivisiä dynaamisia kyselyitä, joita EXECUTE IMMEDIATE ei voi noutaa yksinään.
  • 🧩 DBMS_SQL: Sopii lauseille, joiden sarakemäärä tai -tyyppi on tuntematon ennen suoritusta.
  • 🔐 Sidottavat muuttujat: USING-lauseke välittää arvot paikkatietoisesti ja estää SQL-injektion.
  • 🤖 AI-apu: Tekoälytyökalut luonnostelevat dynaamisen SQL:n ja merkitsevät injektioriskit tarkistuksen aikana.

Oracle PL/SQL Dynaaminen SQL-opetusohjelma

Mikä on dynaaminen SQL?

Dynaaminen SQL on ohjelmointimenetelmä lausekkeiden luomiseen ja suorittamiseen ajonaikana. Sitä käytetään pääasiassa yleiskäyttöisten ja joustavien ohjelmien kirjoittamiseen, joissa SQL-lausekkeet luodaan ja suoritetaan ajonaikana vaatimusten perusteella, esimerkiksi silloin, kun taulukoiden nimiä, sarakeluetteloita tai WHERE-ehtoja ei tiedetä ennen ohjelman suorittamista.

Tapoja kirjoittaa dynaamista SQL:ää

PL/SQL tarjoaa kaksi tapaa kirjoittaa dynaamista SQL:ää:

  1. NDS – Native Dynamic SQL (EXECUTE IMMEDIATE- ja OPEN-FOR-lausekkeet)
  2. DBMS_SQL (toimitettu paketti)

Yleinen sääntö on yksinkertainen: jos syöte- ja tulosmuuttujien lukumäärä ja tietotyypit tiedetään käännösaikana, käytä Native Dynamic SQL:ää, koska se on nopeampi ja vaatii vähemmän koodia. Kun tämä tieto tunnetaan vasta suorituksen aikana, käytä DBMS_SQL-pakettia.

NDS (Native Dynamic SQL) – Suorita välittömästi

Natiivi dynaaminen SQL on helpompi tapa kirjoittaa dynaamista SQL:ää. Se käyttää EXECUTE IMMEDIATE -komentoa SQL:n luomiseen ja suorittamiseen suorituksen aikana. Tämän lähestymistavan käyttämiseksi suorituksen aikana käytettävien muuttujien tietotyyppi ja lukumäärä on tiedettävä etukäteen. Se tarjoaa myös paremman suorituskyvyn ja vähemmän monimutkaisuutta verrattuna DBMS_SQL:ään.

Syntaksi

EXECUTE IMMEDIATE dynamic_sql_string
[INTO {variable[, variable]... | record}]
[USING [IN | OUT | IN OUT] bind_argument[, ...]]
[RETURNING INTO bind_argument[, ...]];
  • dynaaminen_sql_merkkijono: Merkkijonolauseke (VARCHAR2 tai CHAR, ei NVARCHAR2/NCHAR), joka sisältää yhden SQL-lauseen tai PL/SQL-lohkon.
  • INTO-lauseke: Valinnainen. Käytetään vain silloin, kun dynaaminen SQL on yksirivinen SELECT; se tallentaa palautetut arvot muuttujiin tai tietueeseen. Jokaisella valitulla sarakkeella on oltava tyyppiyhteensopiva muuttuja.
  • USING-lauseke: Valinnainen. Tarjoaa sidontamuuttujia. Oletustila on IN; OUT- ja IN OUT-tiloja käytetään arvojen vastaanottamiseen takaisin.
  • PALAUTUSLAUSEKE: Käytetään DML-lauseiden kanssa, joissa on RETURNING-lause, kaappaamaan muuttuvan rivin arvot sidonta-argumentteihin.

Esimerkki 1: Tässä esimerkissä haemme tiedot emp-taulukosta emp_no '1001':lle käyttämällä NDS-lauseketta ja bind-muuttujaa.

NDS - Suorita välittömästi

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

ulostulo

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

Code Selitys:

  • Rivit 2–6: Muuttujien deklarointi.
  • Rivi 8: SQL-komennon kehystäminen suorituksen aikana. SQL sisältää sidontamuuttujan ':empno' WHERE-ehdossa.
  • Rivit 9–11: Kehystetyn SQL-komennon suorittaminen EXECUTE IMMEDIATE -komennolla. INTO-lausekkeen muuttujat sisältävät noudetut arvot ja USING-lauseke antaa arvon sidonta-muuttujalle :empno.
  • Rivit 12–15: Näyttää noudetut arvot.

Dynaamisen SQL:n käyttö DDL:ssä

Staattinen PL/SQL ei voi suorittaa suoraan DDL-komentoja, kuten CREATE, ALTER tai DROP. EXECUTE IMMEDIATE ratkaisee tämän muodostamalla lausekkeen merkkijonona, mikä on kätevää myös silloin, kun objektin nimi annetaan suorituksen aikana:

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

Objektien nimiä (taulukko, sarake, skeema) ei voida välittää sidontamuuttujina, joten ne on yhdistettävä merkkijonoksi. Tarkista aina tällainen syöte, esimerkiksi DBMS_ASSERT.SIMPLE_SQL_NAME:llä, SQL-injektion välttämiseksi.

DBMS_SQL dynaamiselle SQL:lle

PL/SQL tarjoaa DBMS_SQL-paketin dynaamisen SQL:n käsittelyyn, kun lausekkeen rakennetta ei tiedetä ennen suoritusta. Dynaamisen SQL:n luonti- ja suoritusprosessi sisältää seuraavat vaiheet:

  • AVAA KURSORI: Dynaaminen SQL suoritetaan kuten kohdistinSQL-lausekkeen suorittamiseksi meidän on ensin avattava kohdistin.
  • JÄSENTÄÄ SQL: Jäsentää dynaamisen SQL:n. Tämä tarkistaa syntaksin ja pitää kyselyn suoritusvalmiina.
  • BIND-MUUTTUJAN arvot: Määritä sidontamuuttujien arvot, jos niitä on.
  • MÄÄRITÄ SARAKKE: Määrittele jokainen sarake käyttämällä sen suhteellista sijaintia select-lausekkeessa.
  • SUORITTAA: Suorita jäsennetty kysely.
  • NOUDETAVAT ARVOT: Nouda suoritetut arvot.
  • SULJE KURSORI: Kun tulokset on haettu, sulje kursori.

Esimerkki 1: Tässä esimerkissä haemme tiedot emp-taulukosta emp_no '1001':lle käyttämällä DBMS_SQL-lauseketta. EXCEPTION-lohko sulkee kohdistimen, vaikka tapahtuisi virhe.

DBMS_SQL dynaamiselle SQL:lle

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

ulostulo

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

Code Selitys:

  • Rivit 1–8: Muuttujan deklarointi.
  • Rivi 10: SQL-lauseen kehystäminen.
  • Rivi 11: Kohdistimen avaaminen DBMS_SQL.OPEN_CURSOR-funktiolla, joka palauttaa avatun kohdistimen tunnuksen.
  • Rivi 12: Kun kohdistin on avattu, SQL jäsennetään.
  • Rivi 13: Sidonta-arvo '1001' asetetaan ':empno'-arvon tilalle.
  • Rivit 14–17: Sarakkeiden määrittely niiden suhteellisen sijainnin perusteella: (1) työntekijän_nimi, (2) työntekijän_numero, (3) palkka, (4) esimies.
  • Rivi 18: Suoritetaan kysely DBMS_SQL.EXECUTE-metodilla, joka palauttaa käsiteltyjen tietueiden määrän.
  • Rivit 19–32: Tietueiden hakeminen silmukassa. FETCH_ROWS palauttaa arvon 0, kun rivejä ei ole jäljellä, mikä sulkee silmukan.
  • POIKKEUS-lohko: Varmistaa, että kohdistin on suljettu, jotta avoimet kohdistimet eivät vuoda, jos virhe ilmenee.

NDS vs. DBMS_SQL: Milloin käyttää kumpaakin

Molemmat lähestymistavat ajavat SQL:ää ajonaikana, mutta ne sopivat eri tilanteisiin:

  • Käytä natiivia dynaamista SQL:ää (SUORITA HETI / AVAA FOR) kun syötteiden ja tulosteiden lukumäärä ja tietotyypit tiedetään käännösaikana. Se on nopeampaa, helpompaa lukea ja vaatii vähemmän koodia.
  • Käytä DBMS_SQL:ää kun rakenne on tuntematon ajonaikaiseen asti, esimerkiksi kyselyssä, jonka valittujen sarakkeiden tai sidontamuuttujien määrä vaihtelee, joka tunnetaan nimellä method-4 dynaaminen SQL, tai lauseke, joka on liian suuri mahtuakseen yhteen 32 kilotavun VARCHAR2-muuttujaan.

UKK

Sidottavat muuttujat välittävät käyttäjän syötteen datana, eivät koskaan suoritettavana koodina. USING-lauseke antaa arvot paikkatietoisesti, joten haitallinen teksti ei voi muuttaa lausekkeen rakennetta. Sido aina epäluotettava syöte sen yhdistämisen sijaan.

Ei. Oracle sitoo vain data-arvoja, ei objektien nimiä. Yhdistä tunnisteet SQL-merkkijonoksi ja validoi ne DBMS_ASSERT.SIMPLE_SQL_NAME-metodilla suojautuaksesi injektoinnilta.

EXECUTE IMMEDIATE noutaa vain yhden rivin. Useiden rivien kohdalla avaa REF CURSOR OPEN-FOR-lausekkeella, suorita sitten FETCH-lauseke, kunnes %NOTFOUND ilmenee, ja sulje kohdistin.

Lisää RETURNING-lause INSERT-, UPDATE- tai DELETE-käskyyn ja käytä sitten EXECUTE IMMEDIATE -käskyn RETURNING INTO -lausetta tallentaaksesi kyseisten rivien arvot sidonta-argumentteihin.

Dynaaminen SQL lisää jäsennystyön työmäärää, koska lauseet käännetään suorituksen aikana. Sidottujen muuttujien uudelleenkäyttö mahdollistaa Oracle jaa kursoreita ja vähennä vaikeita jäsennyksiä, keeping suorituskyky lähellä staattista SQL:ää.

Merkkijonon on oltava VARCHAR2- tai CHAR-muodossa. Kansallisia merkkityyppejä, kuten NVARCHAR2 ja NCHAR, ei sallita. Yli 32 kilotavun tekstille DBMS_SQL hyväksyy kokoelman VARCHAR2-osia.

Kyllä. Tekoälyavustajat, kuten GitHub Copilot, luonnostelevat EXECUTE IMMEDIATE- ja DBMS_SQL-lohkot tavallisista kehotteista, ehdottavat sidontamuuttujien paikkamerkkejä ja selittävät jokaisen lausekkeen, vaikka kehittäjän tulisi silti tarkistaa tuloste.

Tekoälyllä toimivat koodinlukijat merkitsevät ketjutetut käyttäjäsyötteet ja suosittelevat sidontamuuttujia tai DBMS_ASSERT-tarkistuksia. Ne korostavat riskialttiita malleja tarkistuksen aikana, auttavatping tiimit havaitsevat injektiovirheet ennen käyttöönottoa.

Tiivistä tämä viesti seuraavasti: