Wednesday, 18 January 2017

NATIVE DYNAMIC SQL



What is Dynamic SQL?
Generally programmer knows the SQL statements that are to be executed at the time of writing program. However, in some cases the programmer may not precisely know the command that is to be executed at the time of writing program. Instead the command may vary from execution to execution and it is to be constructed dynamically (at runtime).  An SQL statement that is constructed  “on the fly” is dynamic SQL statement.

Until Oracle8 the only way to execute dynamic SQL was by using DBMS_SQL package. Oracle8i introduced native dynamic SQL, where you can directly place dynamic SQL commands into PL/SQL blocks. Native dynamic SQL is faster then using DBMS_SQL package.

Dynamic SQL should be considered only when static SQL is not possible. Dynamic SQL will impact performance.

Why do we need dynamic SQL?
We should use dynamic SQL where static SQL doesn’t support the operation that we want to perform.

The following are the situations where you may want to use Dynamic SQL:

q  You want to execute an SQL DDL or DCL command that cannot be executed statically. That means the command is not allowed in the PL/SQL block.  For example, you cannot execute DROP TABLE command from a PL/SQL block.
q  You want to access a table or a view but do not know the name of the object until you run the program.
           
An Example of Dynamic SQL
Let’s look at an example. Assume we have to drop a table. But the name of the table to be dropped is not known. The name of the table is formed using the word SALES and four digits year. For example, it the current year is 2000 then table name would be SALES2000.

That means depending upon the current year the name of the table changes. So the DROP TABLE command should drop the table of the current year and the current year is to be taken from the system date.

The solution to this problem is to construct DROP TABLE command as follows:

     cmd :=   ‘DROP TABLE  sales’ || to_char(sysdate,’yyyy’);

The above statement will put the required command into a char variable and then the command in the char variable is to be executed with EXECUTE IMMEDIATE command.

Execute Immediate Statement
This statement is used to execute a command that is a char variable. In the above example we put the required command in a variable called cmd. Now, we can execute the command as follows:

  EXECUTE IMMEDIATE cmd;

Now let us examine the complete syntax of Execute Immediate statement.

EXECUTE IMMEDIATE dynamic_string
[INTO {variable[, variable]... | record}]
[USING [IN | OUT | IN OUT] bind_argument
    [, [IN | OUT | IN OUT] bind_argument]...];


Dynamic_string
Is the string that contains the command to be executed.
Variable | record
Is a variable into which values retrieved by SELECT command are to be copied. If a record is given then complete row is copied into the record. Record must be a variable of user-defined data type or %ROWTYPE% record.
Bind_argument
Is the value that is to be passed to SQL statement that is being executed. This value will replace a placeholder in the command (more on this later).

The following stored procedure takes the name of the table and drops the table.

create or replace procedure droptable ( tablename varchar2) is
  cmd  varchar2(100);
begin
  cmd := 'drop table ' || tablename;
  execute immediate cmd;
end;

You can now call the above procedure to drop a table. You have to supply the name of the table to be dropped. Remember that in PL/SQL DDL commands are not allowed in static SQL, so dynamic SQL is required.

execute droptable('oldsales');

The following is another stored function that takes the year and deletes all rows from the tables whose name is formed as SALESyear. Where year is the value passed to the procedure.

create or replace function deletesalesrows (year number)
return number is
cmd  varchar2(100);
begin
    cmd := 'delete from sales'|| year;
    execute immediate cmd;
    return  sql%rowcount;
end;

The function takes year number and deletes all rows from sales table of that year. It also returns the number of rows deleted from the table.

You can invoke the function to delete rows of 2000 sales table and display the number of rows deleted as follows:

begin
   dbms_output.put_line( deletesalesrows(2000));
end;

Using Placeholders
Placeholders can be used to replace a missing value at runtime. A placeholder is a name preceded by : (colon).  The placeholder is to be replaced with a value through USING clause of EXECUTE IMMEDIATE statement.

The following example deletes all rows of the given table where PRODID is equal to the product id passed.


create or replace procedure DeleteSalesRows
      (tablename varchar2, prodid number) is
cmd varchar2(100);
begin
    cmd := 'delete rows from ' || tablename || ' where prodid = :prodid';
    execute immediate cmd using  prodid;
end;
/

At the time of executing a command that has placeholders, we must pass values that replace placeholders through USING clause of EXECUTE IMMEDIATE command.  In the above example, the value of variable PRODID will replace the placeholder :PRODID and then the command is executed.


Note: The number of placeholders and the number of values passed by USING clause must be equal.

Placeholders and parameters are matched by the position and not by name. That means if the same placeholder is used for twice or more then for each occurrence the value is to be passed by USING clause as show below.

cmd := 'INSERT INTO sometable VALUES (:x, :x, :y, :x)';

EXECUTE IMMEDIATE sql_stmt USING a, a, b, a;
 
You have to pass four values to the above command although there are only two placeholders – X and Y. This is because of the fact that bind arguments are associated with placeholders by position and not by name.  That means the first bind argument is associated with first placeholder and second bind argument with second placeholder and so on.

Note: Placeholders cannot be used for names of schema objects.
 
The following dynamic SQL statement is INVALID.

cmd := ‘drop table :table_name’;

execute immediate cmd using tablename;

Execute a Query Dynamically
So far whatever we have seen is related to non-query SQL statements. Now, let us see how to execute an SQL query dynamically. The difference between executing a non-query and a query is; a query returns one or more rows. In the beginning we will confine our examples to executing single-row query statements.

When a query in executed dynamically using EXECUTE IMMEDIATE command then we have to supply INTO clause to catch the values retrieved by the query.  The following example shows how to use SELECT command with dynamic SQL to retrieve highest and lowest prices at which the given product is sold in the current year.

declare
      cmd  varchar2(100);
      prodid  number(5)  :=  10;
      lprice  number(5);
      hprice  number(5);
begin
      cmd := ‘select  min(price), max(price) from sales’;
      cmd := cmd || to_char(sysdate,’yyyy’) || ‘  where prodid = :1’;

      -- execute the command by sending product id

      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;

While a query is executed using EXECUTE IMMEDIATE command  the following rules will apply:

q  The number of columns to be retrieved should be known at the time of writing program.
q  Only one record can be retrieved as only one set of variable can be passed using INTO.

The following function is used to return salary of the employee from the given table.


create or replace function GetSalary(tablename varchar2, empno number) return number is
  cmd varchar2(100);
  v_sal  number(5);
begin
    cmd := 'select sal from ' || tablename || ' where empno = :empno';
    execute immediate cmd  into  v_sal  using   empno;

    return v_sal;

exception
   when no_data_found then
    return null;
end;


The above function returns NULL if the given employee number is not found in the given table.


Executing multi-row query dynamically
Using simple EXECUTE IMMEDIATE statement we can execute a query that retrieves only one row. However, when we need to execute a query that has the potential to retrieve multiple rows, we need to follow a different process.

To deal with multi-row query dynamically, we have to use a Cursor. All the rows retrieved by the query are copied into cursor. And then using FETCH statement each row will be fetched and processed.

The following are the statements used to execute a multi-row query dynamically.

Open-For statement to open the cursor
The first step is to open a cursor with the query statement.   This statement associates a cursor variable with the given query and executes the query. This statement has USING option to pass values to placeholders.

The complete syntax of OPEN-FOR statement is as follows:

OPEN {cursor_variable}
  FOR dynamic_string
   [USING bind_argument[, bind_argument]...];


Cursor_variable
Is a weakly typed cursor variable, i.e. a variable that doesn’t have any return type.
Dynamic_string
The SELECT command to be executed.
Bind_argument
Is the value to be passed to placeholders of the command.

The following is an example where all rows of SALES table of the current year are retrieved.

DECLARE
   TYPE SalesCursorType IS REF CURSOR;  -- define weak REF CURSOR type
   salescursor  SalesCursorType;  -- declare cursor variable

BEGIN
   OPEN salescursor FOR  -- open cursor variable
      'SELECT  proid, qty, price from sales’ || to_char(sysdate,’yyyy’);
   .
   .
   .

END;

Fetching row from cursor
Fetches one row from the cursor and copies the values of the columns into corresponding variables.

FETCH {cursor_variable } INTO {define_variable[, define_variable]   ... | record};

Cursor_variable            is the cursor variable with which the result of the query is associated.
Define_variable            is the variable into which the value the corresponding columns should be copied.
Record                         is the variable of a user-defined record type or %ROWTYPE%.


To fetch rows from SALESCURSOR that we have defined in the previous step, we have to use Fetch statement as follows:

Loop
    Fetch salescursor into prodid, qty, price;
    Exit when sales_cursor%notfound;   --exit when no more records exist
    -- process the record here
End loop

Closing the cursor
The last step in the process is closing the cursor after it is processed.

CLOSE {cursor_variable};

A sample program
The following program is consolidating all that we have seen regarding how to execute a multi-row query dynamically.

declare
   type salescursortype is ref cursor;  -- define weak ref cursor type
   salescursor  salescursortype;  -- declare cursor variable

   prodid  number(5);
   qty     number(5);
   price   number(5);

 begin

   open salescursor for  -- open cursor variable
      'select  proid, qty, price from sales’ || to_char(sysdate,’yyyy’);

 loop
    fetch salescursor into prodid, qty, price;
    exit when sales_cursor%notfound;  --exit when no more records exist
    -- process the record here
 end loop

 close salescursor;   -- close the cursor after it is processed.

end;   -- end of the block


Dynamic PL/SQL Blocks
You can execute an anonymous PL/SQL block dynamically using EXECUTE IMMEDIATE statement. It is useful in cases where the procedure to be invoked is known only at runtime.

The following procedure invokes one of the two procedure based on the name of the company. For this we assume we already have two procedures with names HIKESAL_A and HIKESAL_B where A and B are company names.

create or replace procedure callhikesal(company varchar2)is
begin
  execute immediate
     'begin
        hikesal_' || company;
     'end;'
end;

The above procedure calls the procedure with the name HIKESAL_A or HIKESAL_B depending on the name of the company – A or B – passed to the procedure.

The same can be done without dynamic SQL but it becomes length and needs to be updated if more there is any change in number of companies.

The following is non-dynamic SQL version of the same procedure.

create or replace procedure callhikesal(company varchar2)is
begin
   if  company = ‘A’ then
        hikesal_a;
   else
        hikesal_b;
   end if;
end;
 
But as you can notice, if we add one more company to the list then the procedure is to be modified to accommodate another company.
 
Summary
Dynamic SQL is the process of constructing SQL commands dynamically and executing them.  This was done using DBMS_SQL package prior to Oracle8i but in Oracle8i Native Dynamic SQL was introduced making dynamic SQL less cumbersome and faster.

EXECUTE IMMEDIATE statement is used to execute an SQL command that is constructed at runtime. You can execute DML, single-row SELECT and even multi-row SELECT.

While executing multi-row query, we have to use a cursor to retrieve the data into cursor and then fetch one row at a time from user.

It is also possible to execute a PL/SQL block dynamically.


SQL Interview Questions and Answers

There is given sql interview questions and answers that has been asked in many companies. For PL/SQL interview questions, visit our next...