Oracle PL/SQL Dynamic SQL Tutorial: Execute Immediate & DBMS_SQL

โšก Smart sammanfattning

Dynamisk SQL i Oracle PL/SQL bygger och kรถr satser vid kรถrning, och anpassar frรฅgor till fรถrรคndrade krav genom tvรฅ metoder: Native Dynamic SQL med EXECUTE IMMEDIATE och OPEN-FOR, och det flexibla DBMS_SQL-paketet fรถr komplexa fall.

  • โš™๏ธ Runtime SQL: Dynamisk SQL genererar och exekverar satser nรคr tabell- eller kolumnnamn รคr okรคnda i fรถrvรคg.
  • โšก Inbyggd dynamisk SQL: EXECUTE IMMEDIATE skapar och kรถr SQL snabbt med minsta mรถjliga kod.
  • ๐Ÿ” ร–PPET Fร–R: Hanterar dynamiska frรฅgor med flera rader som EXECUTE IMMEDIATE inte kan hรคmta ensamma.
  • ๐Ÿงฉ DBMS_SQL: Passar satser vars kolumnantal eller kolumntyper รคr okรคnda fram till kรถrningstid.
  • ๐Ÿ” Bindvariabler: USING-klausulen skickar vรคrden positionellt och blockerar SQL-injektion.
  • ๐Ÿค– AI-hjรคlp: AI-verktyg utarbetar dynamisk SQL och flaggar risker fรถr injektion under granskning.

Oracle Handledning fรถr PL/SQL Dynamisk SQL

Vad รคr dynamisk SQL?

Dynamisk SQL รคr en programmeringsmetodik fรถr att generera och kรถra satser vid kรถrning. Den anvรคnds huvudsakligen fรถr att skriva generella och flexibla program dรคr SQL-satser skapas och kรถrs vid kรถrning baserat pรฅ kravet, till exempel nรคr tabellnamn, kolumnlistor eller WHERE-villkor inte รคr kรคnda fรถrrรคn programmet kรถrs.

Sรคtt att skriva dynamisk SQL

PL/SQL erbjuder tvรฅ sรคtt att skriva dynamisk SQL:

  1. NDS โ€“ Native Dynamic SQL (satserna EXECUTE IMMEDIATE och OPEN-FOR)
  2. DBMS_SQL (ett medfรถljande paket)

Den allmรคnna regeln รคr enkel: om antalet och datatyperna fรถr in- och utdatavariablerna รคr kรคnda vid kompileringstillfรคllet, anvรคnd Native Dynamic SQL eftersom det รคr snabbare och krรคver mindre kod. Nรคr den informationen bara รคr kรคnd vid kรถrning, anvรคnd DBMS_SQL-paketet.

NDS (Native Dynamic SQL) โ€“ Kรถr omedelbart

Native Dynamic SQL รคr det enklaste sรคttet att skriva dynamisk SQL. Det anvรคnder kommandot EXECUTE IMMEDIATE fรถr att skapa och kรถra SQL:en vid kรถrning. Fรถr att anvรคnda den hรคr metoden mรฅste datatypen och antalet variabler som anvรคnds vid kรถrning vara kรคnda i fรถrvรคg. Det ger ocksรฅ bรคttre prestanda och lรคgre komplexitet jรคmfรถrt med DBMS_SQL.

syntax

EXECUTE IMMEDIATE dynamic_sql_string
[INTO {variable[, variable]... | record}]
[USING [IN | OUT | IN OUT] bind_argument[, ...]]
[RETURNING INTO bind_argument[, ...]];
  • dynamisk_sql_strรคng: Ett strรคnguttryck (VARCHAR2 eller CHAR, inte NVARCHAR2/NCHAR) som innehรฅller en enda SQL-sats eller ett PL/SQL-block.
  • INTO-klausul: Valfritt. Anvรคnds endast nรคr den dynamiska SQL-en รคr en SELECT-kod med en rad; den samlar in de returnerade vรคrdena i variabler eller en post. Varje vald kolumn behรถver en typkompatibel variabel.
  • ANVร„NDNINGSKLAUSUL: Valfritt. Tillhandahรฅller bindningsvariabler. Standardlรคget รคr IN; OUT och IN OUT anvรคnds fรถr att ta emot vรคrden tillbaka.
  • ร…TERVร„NDNINGSKLAUSUL: Anvรคnds med DML-satser som innehรฅller en RETURNING-klausul fรถr att fรฅnga upp berรถrda radvรคrden i bindningsargument.

Exempel 1: I det hรคr exemplet hรคmtar vi data frรฅn emp-tabellen fรถr emp_no '1001' med hjรคlp av en NDS-sats med en bind-variabel.

NDS - Utfรถr omedelbart

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

Produktion

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

Code Fรถrklaring:

  • Raderna 2โ€“6: Deklarera variablerna.
  • Linje 8: Ramar in SQL-koden vid kรถrning. SQL-koden innehรฅller bindningsvariabeln ':empno' i WHERE-villkoret.
  • Raderna 9โ€“11: Kรถr den inramade SQL-kommandot med EXECUTE IMMEDIATE. INTO-klausulvariablerna innehรฅller de hรคmtade vรคrdena, och USING-klausulen anger vรคrdet fรถr bindningsvariabeln :empno.
  • Raderna 12โ€“15: Visar de hรคmtade vรคrdena.

Anvรคnda dynamisk SQL fรถr DDL

Statisk PL/SQL kan inte kรถra DDL som CREATE, ALTER eller DROP direkt. EXECUTE IMMEDIATE lรถser detta genom att skapa kommandot som en strรคng, vilket ocksรฅ รคr praktiskt nรคr ett objektnamn anges vid kรถrning:

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

Objektnamn (tabell, kolumn, schema) kan inte skickas som bindningsvariabler, sรฅ de mรฅste sammanfogas till strรคngen. Validera alltid sรฅdan inmatning, till exempel med DBMS_ASSERT.SIMPLE_SQL_NAME, fรถr att undvika SQL-injektion.

DBMS_SQL fรถr dynamisk SQL

PL/SQL tillhandahรฅller DBMS_SQL-paketet fรถr att arbeta med dynamisk SQL nรคr satsens struktur inte รคr kรคnd fรถrrรคn vid kรถrning. Processen att skapa och kรถra den dynamiska SQL-en innefattar fรถljande steg:

  • ร–PPNA MARKร–R: Den dynamiska SQL-funktionen kรถrs som en markรถrenFรถr att kรถra SQL-satsen mรฅste vi fรถrst รถppna markรถren.
  • PARSE SQL: Parsa den dynamiska SQL-filen. Detta kontrollerar syntaxen och hรฅller frรฅgan redo att kรถras.
  • BINDVARIABEL Vรคrden: Tilldela vรคrdena fรถr bindningsvariablerna, om nรฅgra.
  • DEFINERA KOLUMN: Definiera varje kolumn med hjรคlp av dess relativa position i select-satsen.
  • UTFร–RA: Kรถr den analyserade frรฅgan.
  • Hร„MTA Vร„RDEN: Hรคmta de exekverade vรคrdena.
  • STร„NG MARKร–REN: Nรคr resultaten har hรคmtats, stรคng markรถren.

Exempel 1: I det hรคr exemplet hรคmtar vi data frรฅn emp-tabellen fรถr emp_no '1001' med hjรคlp av en DBMS_SQL-sats. EXCEPTION-blocket stรคnger markรถren รคven om ett fel uppstรฅr.

DBMS_SQL fรถr dynamisk SQL

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

Produktion

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

Code Fรถrklaring:

  • Raderna 1โ€“8: Variabeldeklaration.
  • Linje 10: Rama in SQL-satsen.
  • Linje 11: ร–ppna markรถren med DBMS_SQL.OPEN_CURSOR, vilket returnerar ID:t fรถr den รถppnade markรถren.
  • Linje 12: Efter att markรถren har รถppnats analyseras SQL-koden.
  • Linje 13: Bindningsvรคrdet '1001' tilldelas istรคllet fรถr ':empno'.
  • Raderna 14โ€“17: Definiera kolumnerna efter deras relativa position: (1) anstรคllningsnamn, (2) anstรคllningsnr, (3) lรถn, (4) chef.
  • Linje 18: Kรถr frรฅgan med DBMS_SQL.EXECUTE, vilket returnerar antalet bearbetade poster.
  • Raderna 19โ€“32: Hรคmtar posterna i en loop. FETCH_ROWS returnerar 0 nรคr inga rader finns kvar, vilket avslutar loopen.
  • UNDANTAGSblock: Sรคkerstรคller att markรถren รคr stรคngd sรฅ att รถppna markรถrer inte lรคcker om ett fel uppstรฅr.

NDS vs DBMS_SQL: Nรคr ska man anvรคnda vilket

Bรฅda metoderna kรถr SQL vid kรถrning, men de passar olika situationer:

  • Anvรคnd nativ dynamisk SQL (EXEKTERA OMEDELBART / ร–PPNA Fร–R) nรคr antalet och datatyperna fรถr in- och utdata รคr kรคnda vid kompileringstillfรคllet. Det รคr snabbare, lรคttare att lรคsa och krรคver mindre kod.
  • Anvรคnd DBMS_SQL nรคr strukturen รคr okรคnd fram till kรถrning, till exempel en frรฅga vars antalet valda kolumner eller bindningsvariabler varierar, sรฅ kallad dynamisk SQL fรถr metod 4, eller ett uttalande som รคr fรถr stort fรถr att fรฅ plats i en enda VARCHAR2-variabel pรฅ 32K.

Vanliga frรฅgor

Bindvariabler skickar anvรคndarinmatning som data, aldrig som kรถrbar kod. USING-klausulen tillhandahรฅller vรคrden positionellt, sรฅ skadlig text kan inte รคndra satsstrukturen. Bind alltid otillfรถrlitlig inmatning istรคllet fรถr att sammanfoga den.

Nej. Oracle binder endast datavรคrden, inte objektnamn. Sammanfoga identifierare till SQL-strรคngen och validera dem med DBMS_ASSERT.SIMPLE_SQL_NAME fรถr att undvika injektion.

EXECUTE IMMEDIATE hรคmtar endast en rad. Fรถr mรฅnga rader, รถppna en REF CURSOR med OPEN-FOR-satsen, loopa sedan igenom FETCH tills %NOTFOUND och CLOSE markรถren.

Lรคgg till en RETURNING-klausul i INSERT-, UPDATE- eller DELETE-klausulen och anvรคnd sedan RETURNING INTO-klausulen i EXECUTE IMMEDIATE fรถr att samla in de berรถrda radvรคrdena i bindningsargument.

Dynamisk SQL lรคgger till parsningsoverhead eftersom satser kompileras vid kรถrning. ร…teranvรคndning av bindningsvariabler lรฅter Oracle dela markรถrer och minska hรฅrda parsningar, keeping prestanda nรคra statisk SQL.

Strรคngen mรฅste vara VARCHAR2 eller CHAR. Nationella teckentyper som NVARCHAR2 och NCHAR รคr inte tillรฅtna. Fรถr text รถver 32K accepterar DBMS_SQL en samling av VARCHAR2-delar.

Ja. AI-assistenter som GitHub Copilot utarbetar EXECUTE IMMEDIATE- och DBMS_SQL-block frรฅn vanliga prompter, fรถreslรฅr platshรฅllare fรถr bindvariabler och fรถrklarar varje klausul, รคven om en utvecklare fortfarande bรถr granska utdata.

AI-drivna kodskannrar flaggar sammanfogade anvรคndarinmatningar och rekommenderar bindningsvariabler eller DBMS_ASSERT-kontroller. De lyfter fram riskfyllda mรถnster under granskning, hjรคlpping team upptรคcker injektionsfel fรถre driftsรคttning.

Sammanfatta detta inlรคgg med: