Learning Objectives – after this chapter you will be able to:
  • Explain dynamic SQL, why it is needed and how native dynamic SQL differs from DBMS_SQL.
  • Execute DDL and DML at run time with EXECUTE IMMEDIATE.
  • Use placeholders with the USING clause and retrieve values with INTO.
  • Execute multi-row queries dynamically with a REF CURSOR and OPEN-FOR, FETCH, CLOSE.
  • Execute PL/SQL blocks dynamically.
What is Dynamic SQL?

Usually the SQL statements of a program are known when the program is written (static SQL). Sometimes the command varies from execution to execution and must be constructed at run time – an SQL statement constructed “on the fly” is a dynamic SQL statement.

  • Until Oracle8 the only way was the DBMS_SQL package. Oracle8i introduced native dynamic SQL – dynamic statements placed directly in PL/SQL – which is faster than DBMS_SQL.
  • Dynamic SQL affects performance; use it only when static SQL is not possible.
When do we need it?
  • To execute DDL or DCL commands that are not allowed statically in PL/SQL (e.g. DROP TABLE).
  • To access a table or view whose name is known only at run time (e.g. SALES2000, SALES2001 … by year).
EXECUTE IMMEDIATE
EXECUTE IMMEDIATE dynamic_string [INTO {variable [, variable] ... | record}] [USING [IN | OUT | IN OUT] bind_argument [, ...]];
DDL and DML built at run time
create or replace procedure droptable (tablename varchar2) is
   cmd varchar2(100);
begin
   cmd := 'drop table ' || tablename;
   execute immediate cmd;
end;
/
execute droptable('oldsales');

create or replace function deletesalesrows (year number) return number is
   cmd varchar2(100);
begin
   cmd := 'delete from sales' || year;      -- e.g. SALES2000
   execute immediate cmd;
   return sql%rowcount;                     -- rows deleted
end;
Placeholders and the USING Clause

A placeholder is a name preceded by a colon (:prodid) that is replaced at run time by a value given in the USING clause.

Bind arguments
create or replace procedure deletesalesrows2 (tablename varchar2, prodid number) is
   cmd varchar2(100);
begin
   cmd := 'delete from ' || tablename || ' where prodid = :prodid';
   execute immediate cmd using prodid;
end;
/
-- placeholders are matched by POSITION, not by name:
cmd := 'insert into sometable values (:x, :x, :y, :x)';
execute immediate cmd using a, a, b, a;     -- four bind arguments

-- INVALID: placeholders cannot stand for schema object names
-- execute immediate 'drop table :table_name' using tablename;
  • The number of placeholders (occurrences) must equal the number of values in USING.
  • Placeholders cannot be used for the names of schema objects – concatenate names into the string instead.
Executing a Query Dynamically
Single-row query with INTO
declare
   cmd    varchar2(100);
   prodid number(5) := 10;
   lprice number(5);
   hprice number(5);
begin
   cmd := 'select min(price), max(price) from sales'
          || to_char(sysdate,'yyyy') || ' where prodid = :1';
   execute immediate cmd into lprice, hprice using prodid;
   dbms_output.put_line('lowest price is : '  || lprice);
   dbms_output.put_line('highest price is : ' || hprice);
end;

With EXECUTE IMMEDIATE the number of columns must be known when writing the program and only one row can be retrieved (one set of INTO variables). A function such as GETSALARY can return NULL by handling NO_DATA_FOUND.

Executing a Multi-row Query Dynamically

A query that may return many rows needs a cursor variable of a weak REF CURSOR type (no return type):

  1. OPEN cursor_variable FOR dynamic_string [USING bind_arguments]; – associates the cursor variable with the query and executes it.
  2. FETCH cursor_variable INTO variables | record; – in a loop, exiting on %NOTFOUND.
  3. CLOSE cursor_variable;
OPEN-FOR, FETCH and CLOSE
declare
   type salescursortype is ref cursor;     -- weak REF CURSOR type
   salescursor salescursortype;            -- cursor variable
   prodid number(5);
   qty    number(5);
   price  number(5);
begin
   open salescursor for
      'select prodid, qty, price from sales' || to_char(sysdate,'yyyy');
   loop
      fetch salescursor into prodid, qty, price;
      exit when salescursor%notfound;
      -- process the record here
   end loop;
   close salescursor;
end;
Dynamic PL/SQL Blocks

EXECUTE IMMEDIATE can also run an anonymous PL/SQL block – useful when the procedure to call is known only at run time. The static alternative (IF company = 'A' THEN hikesal_a …) must be edited whenever a company is added.

Calling HIKESAL_A or HIKESAL_B by name
create or replace procedure callhikesal (company varchar2) is
begin
   execute immediate 'begin hikesal_' || company || '; end;';
end;
A few listings in the PDF contain typing slips (e.g. delete rows from, mismatched quotes in the dynamic block, sales_cursor%notfound). The code on this page shows the corrected statements.
Key Takeaways
  • Native dynamic SQL (Oracle8i) replaces DBMS_SQL for most needs and is faster.
  • EXECUTE IMMEDIATE runs DDL, DML, single-row queries (INTO) and PL/SQL blocks.
  • USING binds values to placeholders by position; object names cannot be placeholders.
  • Multi-row dynamic queries use a weak REF CURSOR with OPEN-FOR / FETCH / CLOSE.
Chapter 24 Notes (PDF)
ch24.pdf 8 pages 265 KB Open in PDF Viewer (Free Tool)

Your browser cannot show this PDF here. Open Chapter 24 Notes (PDF)

ch24.pdfKeys: ← → page · + − zoom · F full screen
Question / Answer (15 questions)

Moderate-level questions based on the Chapter 24 notes (PDF). Click a question to see the answer.

Answer : Normally the programmer knows the SQL statements when writing the program. When the command varies from execution to execution and must be constructed at run time, the SQL statement constructed "on the fly" is called a dynamic SQL statement.

Answer : Until Oracle8 the only way to execute dynamic SQL was the DBMS_SQL package. Oracle8i introduced native dynamic SQL, where dynamic SQL commands are placed directly into PL/SQL blocks; native dynamic SQL is faster than using DBMS_SQL.

Answer : Only when static SQL does not support the required operation, because dynamic SQL affects performance – e.g. to execute a DDL or DCL command that cannot be executed statically in PL/SQL (such as DROP TABLE), or to access a table or view whose name is not known until the program runs.

Answer : A sales table whose name is SALES followed by the four-digit year, e.g. SALES2000. The command is built as cmd := 'DROP TABLE sales' || TO_CHAR(SYSDATE, 'yyyy') and executed with EXECUTE IMMEDIATE cmd.

Answer : EXECUTE IMMEDIATE dynamic_string [INTO {variable[, variable]... | record}] [USING [IN | OUT | IN OUT] bind_argument [, ...]]. dynamic_string contains the command to execute; INTO gives the variables (or a record of a user-defined type or %ROWTYPE) that receive values retrieved by a SELECT; bind_argument values replace the placeholders in the command.

Answer : DDL commands are not allowed in static SQL in PL/SQL. The procedure therefore builds the command as a string (cmd := 'drop table ' || tablename) and executes it with EXECUTE IMMEDIATE cmd, e.g. EXECUTE droptable('oldsales').

Answer : After EXECUTE IMMEDIATE runs the DELETE, the implicit cursor attribute SQL%ROWCOUNT gives the number of rows affected, which the function returns – e.g. DELETESALESROWS(2000) deletes rows from SALES2000 and returns the count.

Answer : A placeholder is a name preceded by a colon (e.g. :prodid) used in the dynamic command to stand for a value missing at writing time. Its value is supplied through the USING clause of EXECUTE IMMEDIATE, e.g. EXECUTE IMMEDIATE cmd USING prodid.

Answer : Placeholders are matched with bind arguments by position, not by name, so a value must be passed for every occurrence. If 2 placeholders are used 3 times in total, 3 bind arguments must be passed (e.g. :x, :x, :y, :x needs USING a, a, b, a – four values for four occurrences).

Answer : No. Placeholders cannot be used for names of schema objects, so 'drop table :table_name' with USING tablename is invalid; the name must be concatenated into the command string.

Answer : With EXECUTE IMMEDIATE and an INTO clause to catch the values, e.g. EXECUTE IMMEDIATE cmd INTO lprice, hprice USING prodid for "select min(price), max(price) from sales<year> where prodid = :1".

Answer : The number of columns to be retrieved must be known when writing the program, and only one record can be retrieved, because only one set of variables can be passed using INTO. If no row is found NO_DATA_FOUND can be handled (e.g. GETSALARY returns NULL).

Answer : Using a cursor variable of a weakly typed REF CURSOR type (TYPE salescursortype IS REF CURSOR). OPEN cursor_variable FOR dynamic_string [USING bind_arguments] executes the query; FETCH cursor_variable INTO variables in a loop (EXIT WHEN cursor_variable%NOTFOUND) processes each row; CLOSE cursor_variable closes it.

Answer : It is a cursor variable whose REF CURSOR type does not have any return type, so it can be opened for any query string at run time with OPEN-FOR.

Answer : An anonymous block is placed in a string and executed with EXECUTE IMMEDIATE, e.g. 'begin hikesal_' || company || '; end;' calls HIKESAL_A or HIKESAL_B depending on the company. This is useful when the procedure to invoke is known only at run time; the static IF version must be modified whenever a company is added.
MCQ Test : Check Your Understanding (5 questions)

Select an option and press Check Answer, or answer all questions and press Submit Test to see your score. Each answer comes with an explanation.

Q1. Which option is used to pass bind arguments to placeholders?

Show Answer

Correct Answer : A. USING
The USING clause of EXECUTE IMMEDIATE (or OPEN-FOR) supplies values for placeholders.

Q2. Which release introduced native dynamic SQL?

Show Answer

Correct Answer : B. Oracle8i
Until Oracle8 DBMS_SQL was the only way; Oracle8i introduced native dynamic SQL.

Q3. Placeholders and bind arguments are matched by:

Show Answer

Correct Answer : C. Position
Bind arguments are associated with placeholders by position, not by name.

Q4. Which statement executes a multi-row query dynamically?

Show Answer

Correct Answer : D. OPEN cursor_variable FOR dynamic_string
OPEN-FOR with a weak REF CURSOR variable executes a multi-row query; rows are then fetched in a loop.

Q5. Which of the following is INVALID?

Show Answer

Correct Answer : A. execute immediate 'drop table :t' using tname;
Placeholders cannot be used for names of schema objects such as table names.

Bibliography/References

The chapter notes PDF is the course study material and is credited to its author below. The explanations, questions and MCQs on this page are original study aids written for this course from the topics covered in that PDF. The other works are cited for verification and further reading. Oracle, SQL*Plus and PL/SQL are trademarks of Oracle Corporation; other names belong to their respective owners.

  1. Srikanth, P. (n.d.). Oracle For Beginners (chapter notes – the PDF shown on each chapter page). Srikanth Technologies. http://www.srikanthtechnologies.com/
  2. Oracle Corporation. (n.d.). Oracle Database PL/SQL Language Reference, 19c. https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/
  3. Oracle Corporation. (n.d.). Oracle Database PL/SQL Packages and Types Reference, 19c. https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/
  4. Feuerstein, S., & Pribyl, B. (2014). Oracle PL/SQL Programming (6th ed.). O’Reilly Media.