Dynamic SQL table name as variable - sql

I have this procedure which is working, 2 parameters can be passed when calling procedure and it executes the select query.
create or replace procedure dynamic_sql
(input1 varchar2, input2 varchar2)
as begin
execute immediate
'select :variable1, :variable2 from emp'
using input1,input2;
end;
/
exec dynamic_sql('ename','job');
In the same way I try to add third variable which will replace the table Emp, but it doesn't work, passed in table name is 100% correct. This is the code that doesn't work (ORA-00903: invalid table name):
create or replace procedure dynamic_sql
(input1 varchar2, input2 varchar2,input_table varchar2)
as begin
execute immediate
'select :variable1, :variable2 from :variable3'
using input1,input2,input_table;
end;
/
exec dynamic_sql('ename','job','emp');

Try something like this:
Its due to the parsing of the table name before execution.
create or replace procedure dynamic_sql
(input1 varchar2, input2 varchar2,input_table varchar2)
as
str varchar2(1000) := NUll;
begin
str := 'select '||input1||','|| input2 ||' from '||input_table;
execute immediate str;
end;
/
exec dynamic_sql('ename','job','emp');
Procedure created.
PL/SQL procedure successfully completed.

Related

Create stored procedure which accepts input as stored procedure and produce results dynamically

I want to implement this below requirements:
Sample procedure #1:
Procedure Proc1(Input1 VARCHAR2,
Input2 NUMBER,
Input3 VARCHAR2,
Output Sys_RefCursor)
IS
BEGIN
..................
..................
..................
END;
Sample procedure #2:
Procedure Proc2(Ipval1 VARCHAR2,
Ipval2 NUMBER,
Ipval3 VARCHAR3
Output Sys_RefCursor)
IS
Begin
..................
..................
..................
End;
Now the requirement is, I want to create a stored procedure which should accept the procedure name as input.
So according to the input procedure name, it should dynamically produce the result of it.
Procedure GenProc(InputProcedureName VARCHAR2,
InputListVal VARCHAR2,
OutputCur SYS_REFCURSOR)
IS
Begin
................Dynamic Code ............... To produce result set as per input procedurename
END;
Any idea how to write dynamic code to fulfill this requirement?
Let's say here:
InputProcedureName -- Can be Proc1 or Proc2 ,
InputListVal -- List of input given in comma separated value for the input procedurenames,
OutputCur -- Should produce output list dynamically as per the input procedurename
Something like this:
create or replace procedure Proc1 (
par1 varchar2,
par2 number,
par3 varchar2,
rc out sys_refcursor) is
begin
open rc for
select 'executing Proc1('||par1||','||par2||','||par3||')' result from dual;
end;
/
create or replace procedure execProc (
procname varchar2,
args sys.odciVarchar2List,
rc out sys_refcursor) is
begin
execute immediate 'begin '||procname||q'[(:s1, :n2, :s3, :rc); end;]'
using args(1), to_number (args(2)), args(3), out rc;
end;
/
The execution and outcomes:
set autoprint on
var rc refcursor
exec execProc ('Proc1', sys.odciVarchar2List ('abc', 123, 'def'), :rc)
RESULT
--------------------------------
executing Proc1(abc,123,def)
Disclaimer: This answer is meant for educational purposes only!!!

Anonymous PL/SQL Block to call a procedure with collection

I need to test a package procedure which accepts a PL/SQL table and returns SYS_REFCURSOR.
What I am looking for is Anonymous PL/SQL block which does the following :
populates PL/SQL table and pass to procedure
The relevant code is below:
create or replace type rec_typ as object
(
fname varchar2(10),
lname varchar2(10)
)
/
create or replace type rec_arr as table of rec_typ
/
create or replace package get_emp_pkg
as
procedure get_emp
(
l_rec rec_arr,
p_out out sys_refcursor
);
end;
/
create or replace package body get_emp_pkg
as
procedure get_emp
(
l_rec rec_arr,
p_out out sys_refcursor
)
as
l_out rec_arr;
begin
open p_out for select * from table ( l_out );
end;
end;
/
You need to declare and populate a table collection, and declare a cursor variable; and then pass those into the procedure call; e.g.:
declare
l_rec rec_arr;
l_cur sys_refcursor;
begin
l_rec := rec_arr(rec_typ('Joe','Bloggs'), rec_typ('Mary','Berry'));
get_emp_pkg.get_emp(l_rec, l_cur);
-- do something with the cursor
close l_cur;
end;
/
If you are just testing it and you're using SQL*Plus or SQL Developer you could simplify this to use a client bind variable:
variable cur refcursor;
declare
l_rec rec_arr;
begin
l_rec := rec_arr(rec_typ('Joe','Bloggs'), rec_typ('Mary','Berry'));
get_emp_pkg.get_emp(l_rec, :cur);
end;
/
print cur;
or even more simply:
variable cur refcursor;
begin
get_emp_pkg.get_emp(rec_arr(rec_typ('Joe','Bloggs'), rec_typ('Mary','Berry')), :cur);
end;
/
print cur;
and you could even replace that with an execute call, which is just a wrapper for an anonymous block anyway, so functionally almost identical.
Your procedure has a mistake though; you're declaring a local l_out variable and then opening the ref cursor based on that, but you don't ever populate it - so the ref cursor result set will always be empty. If you change it to use l_rec instead (though I would call that p_rec, with the p_ prefix indicating a parameters and leave the l_ prefix for local variables):
create or replace package body get_emp_pkg
as
procedure get_emp
(
l_rec rec_arr,
p_out out sys_refcursor
)
as
begin
open p_out for select * from table ( l_rec );
end get_emp;
end;
/
then the bind variable versions above both see:
PL/SQL procedure successfully completed.
FNAME LNAME
---------- ----------
Joe Bloggs
Mary Berry

How to execute a local procedure using execute immedate?

I have the below PL SQL Block:
WHENEVER SQLERROR EXIT 1
SET SERVEROUTPUT ON
DECLARE
v_sql VARCHAR2(500);
f1 VARCHAR2(20) := 'abc';
p_procname VARCHAR2 (30) := 'OPENLOG';
PROCEDURE OPENLOG (file_name IN VARCHAR2)
IS
BEGIN
NULL;
END;
BEGIN
DBMS_OUTPUT.PUT_LINE('Begin');
v_sql := 'BEGIN ' || p_procname || '(:a); END;';
EXECUTE IMMEDIATE v_sql USING IN f1;
END;
/
When I execute the above block, I get the error:
DECLARE
*
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00201: identifier 'OPENLOG' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
ORA-06512: at line 19
However, if the procedure OPENLOG is part of a package, then it works fine.
Please advise how to execute a local procedure using dynamic SQL.
As Amarillo said you can't execute a locally-defined procedure dynamically, as it doesn't exist in the SQL scope the dynamic section will be using.
The situation you describe is that all the procedures are defined in the anonymous block's DECLARE section and you are running a query that tells you which of them to execute - and presumably which also gives you the arguments to pass. You can just use an if/else construct or a case statement to execute the appropriate procedures, something like:
DECLARE
...
BEGIN
FOR data IN (SELECT procname, arg1, arg2, ... from <your_query>) LOOP
CASE data.procname
WHEN 'OPENLOG' THEN
openlog(data.arg1);
WHEN 'WRITELOG' THEN
writelog(data.arg1, data.arg2);
WHEN ...
...
ELSE
-- handle/report an invalid procedure name
-- or skip the `ELSE` and let CASE_NOT_FOUND be thrown
END CASE;
END LOOP;
END;
/
You just need one WHEN condition and appropriate procedure call for each procedure. You can also either have an ELSE to catch any unexpected procedure names or let the CASE_NOT_FOUND exception (ORA-06592) be thrown, depending on what you need to happen if that ever occurs.
Use it like this:
DECLARE
v_sql VARCHAR2(500);
f1 VARCHAR2(20) := 'abc';
p_procname VARCHAR2 (30) := 'OPENLOG';
PROCEDURE OPENLOG (file_name IN VARCHAR2)
IS
BEGIN
NULL;
END;
BEGIN
DBMS_OUTPUT.PUT_LINE('Begin');
openlog(f1);
END;
You don't need to use execute immediate with begin end in this case, because you have the procedure in the declare section.
The other way is create the procedure as a database object like this:
CREATE PROCEDURE OPENLOG (file_name IN VARCHAR2)
IS
BEGIN
NULL;
END;
And the you can use execute immediate:
DECLARE
v_sql VARCHAR2(500);
f1 VARCHAR2(20) := 'abc';
p_procname VARCHAR2 (30) := 'OPENLOG';
BEGIN
DBMS_OUTPUT.PUT_LINE('Begin');
v_sql := 'BEGIN ' || p_procname || '(:a); END;';
EXECUTE IMMEDIATE v_sql USING IN f1;
END;

Set an out parameter value from a pl/sql block stored in a clob column

I have the following table and relevant column..
TABLEA
SQL_SCRIPT CLOB
Here is a procedure that executes the pl/sql block in the clob..
Create procedure (p_exit_code IN OUT NUMBER)
AS
V_sql_val sql_script%TYPE;
…
BEGIN
Select sql_script into v_sql_val from tablea;
EXECUTE IMMEDIATE sql_script;
END;
In SQL_SCRIPT I am trying to do following in the exception handler:
EXCEPTION
WHEN EXCP_STOP_PROCESS THEN
p_exit_code := 1;
END;
I need to set the value of p_exit_code parameter in procedure from the clob script. How can I do this?
If I understand correctly, you need to set the value of a variable within sql_script and return this to the calling procedure. This can be achieved using bind variables. A very simple test case is below:
declare
-- ':return_code' is the bind variable
sql_script varchar2(1000) := 'begin :return_code := 1; end;';
vn_result number;
begin
execute immediate sql_script using out vn_result;
dbms_output.put_line(vn_result);
end;

Create stored procedure which returns in one OUT param database version (Oracle)

I new with PL\SQL so i'm traying to make a procedure witch return database version with OUT in one param. My SQL code is:
CREATE OR REPLACE PROCEDURE getVersion(outParam1 OUT VARCHAR2)
IS
BEGIN
outParam1 := SELECT * FROM V$VERSION;
END;
Can some one explain me how to write procedure right way and how to execute the procedure.
Try as
CREATE OR REPLACE PROCEDURE getVersion (out_cursor OUT SYS_REFCURSOR)
AS
BEGIN
OPEN out_cursor FOR SELECT * FROM V$VERSION;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line (SQLERRM);
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END getVersion;
In order to execute from SQL Plus, do as
VARIABLE mycursor REFCURSOR;
BEGIN
getVersion (:mycursor);
END;
/
PRINT mycursor
From SQL Developer do as
var mycursor refcursor
exec getVersion(:mycursor)
print mycursor
If you would like to have OUT varchar2 with a single row, then you could something like
CREATE OR REPLACE PROCEDURE myProc (outParam1 OUT VARCHAR2)
AS
BEGIN
SELECT dummy INTO outParam1 FROM DUAL;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line (SQLERRM);
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END myProc;
and execute as
VARIABLE testvar VARCHAR2(16);
EXEC myProc(:testvar);
PRINT testvar;