SELECTing inside a stored procedure - oracle

I expected the code like:
create or replace procedure dmp(t in varchar2)
AS
BEGIN
EXECUTE IMMEDIATE 'SELECT * FROM ' || t;
END;
/
BEGIN
dmp('SOMETABLE');
END;
to be the same as SELECT * FROM SOMETABLE. However, calling the stored procedure does not actually output anything -- for any table, including the obviously non-empty ones... Why is that? How would I write a stored procedure, that would output result(s) of queries inside it?

Assuming that you are using a client like SQL*Plus or one that supports a subset of SQL*Plus commands like SQL Developer, you can do something like this (note that I am ignoring the potential for SQL injection attacks).
variable rc refcursor;
/
create or replace procedure get_cursor( p_tableName in varchar2,
p_rc out sys_refcursor )
as
begin
open p_rc for 'select * from ' || p_tableName;
end;
/
begin
get_cursor( 'dual', :rc );
end;
/
print rc;

Related

Using collection inside dynamic sql

I am trying to pass dbms_sql.number_table from one procedure to another and then using it inside a dynamic plsql block. But the below code throws error as:
Error(6,17): PLS-00306: wrong number or types of arguments in call to '||'
create or replace
procedure proc1( v_in_table_name varchar2,
v_in_column_name varchar2,
v_in dbms_sql.number_table)
as
plsql_block varchar2(4000);
begin
plsql_block:='declare
begin
FORALL INDX IN 1 ..'||v_in.count||' SAVE EXCEPTIONS
UPDATE '||v_in_table_name||'
Set '||v_in_column_name||'=123 WHERE col2='||v_in||'(INDX)||;
end';
execute immediate plsql_block;
end proc1;
You should make two changes:
First(and its case of error) its when you concatinate string with collection.
If you want to send collection into pl/sql block you shoud use the param.
And second you should add ; in the end of dynamic pl\sql:
create or replace
procedure proc1(v_in dbms_sql.number_table)
as
plsql_block varchar2(4000);
begin
plsql_block:='declare
l_collect dbms_sql.number_table := :number_table ;
begin
FORALL INDX IN 1 ..'||v_in.count||' SAVE EXCEPTIONS
UPDATE table_name
Set col1=123 WHERE col2=l_collect(INDX);
end;';
execute immediate plsql_block using v_in;
end proc1;
But after all changes I would like to ask: Are you realy need to use dynamic pl\sql?
There is no need for using a dynamic block. Also i dont think it can work as well. You can use this way;
create or replace
procedure proc1(v_in dbms_sql.number_table)
as
plsql_block varchar2(4000);
l_collect dbms_sql.number_table;
begin
l_collect := v_in;
FORALL INDX IN 1 ..l_collect.count SAVE EXCEPTIONS
UPDATE table_name
Set col1=123
WHERE col2=l_collect(INDX);
commit;
end proc1;
Execution:
SQL> DECLARE
var DBMS_SQL.number_table;
BEGIN
var (1) := 1;
var (2) := 2;
var (3) := 3;
proc1 (var);
END;
/
PL/SQL procedure successfully completed.
EDIT: As per your edit the code becomes like:
create or replace procedure proc1 (v_in_table_name varchar2,
v_in_column_name varchar2,
v_in dbms_sql.number_table)
as
plsql_block varchar2 (4000);
begin
plsql_block := q'[ FORALL INDX IN 1 ..v_in.count SAVE EXCEPTIONS
UPDATE :table_name
Set :col1=123
WHERE col2=v_in(INDX)]';
execute immediate plsql_block using v_in_table_name, v_in_column_name;
commit;
end proc1;

Retrieve next value of Oracle sequence via stored procedure

I have created a SEQUENCE in Oracle11g which is working as expected. The challenge is that I need to retrieve the next value of the sequence via a stored procedure (because the application I'm using can only call a stored procedure not a sql statement).
I have looked all over and the closest I've found is this (here: https://community.oracle.com/thread/2216649):
CREATE OR REPLACE PROCEDURE CSDWH.CSDWH_RETURN_SEQ_NUM
(SEQ_NAME IN VARCHAR2,SEQUENCE_OUT OUT NUMBER)
IS
BEGIN
EXECUTE IMMEDIATE 'SELECT ' || SEQ_NAME || '.NEXTVAL from dual' INTO sequence_out;
END;
The issue with this is the way of execution:
declare
l_val number;
begin
csdwh_return_seq_num( 'FOO_SEQ', l_val );
dbms_output.put_line( l_val );
end;
The application I need to execute the stored procedure only have a connection string alias, a stored procedure name, and a return parameter field to be specified. The way to execute this stored procedure required 6 lines of code.
Is there any way to achieve the above with the EXECUTE command?
i.e. EXECUTE CSDWH.CSDWH_RETURN_SEQ_NUM
that would return the value?
just create a function instead of procedure
CREATE OR REPLACE FUNCTION GET_SEQ(SEQ_NAME IN VAECHAR2) AS
DECLARE
seq_out NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT ' || SEQ_NAME || '.NEXTVAL from dual' INTO seq_out;
RETURN seq_out;
END;
then you can use select get_seq('foo_seq') from dual

how to execute pl/sql stored procedure havaing sys_refcursor as out parameter

create or replace procedure get_emp(p_deptno number,emp_det out sys_refcursor)
is
begin
open emp_det for select * from emp where deptno=p_deptno;
end;
/
how to pass parameters for sys_refcursor and how to print that result using anonymous blocks in oracle pl/sql
Just declare a REFCURSOR variable and pass it to ur procedure
You can open
DECLARE
MYCUR SYS_REFCURSOR;
BEGIN
get_emp(123,MYCUR);
FOR I in MYCUR
LOOP
-- ur statements
END LOOP;
END;
/
Also like this using SQLPLUS. Output would be like you run a select query, with what ever sent in the cursor.
VARIABLE MYCUR REFCURSOR;
EXECUTE get_emp(:MYCUR);
print MYCUR;

Run oracle procedure with cursor

I am not experienced with db (actually not at all) and I face a problem:
I have oracle 11g and I am using PL/SQL developer.
I have a simple procedure :
type t_ref_cursor is ref cursor;
procedure fakeProc (
io_cursor in out t_ref_cursor
)
is
begin
open io_cursor for
SELECT * from myTable;
end fakeProc;
Now I want to run it as a SQL window (not in a test window)
What I am trying to run:
v_cur cursor;
begin
fakeProc(:v_cur);
end;
I get errors:
ORA-00900:Invalid SQL statement
ORA-01008:not all variables bound
So can you point me the right way to run a procedure like this(with begin -end)?
Use something like this :
declare v_cur SYS_REFCURSOR;
begin
fakeProc(v_cur);
end;
And the procedure looks like:
CREATE OR REPLACE PROCEDURE FAKEPROC(
io_cursor in out SYS_REFCURSOR
)
IS
begin
open io_cursor for
SELECT * from resource_map;
END FAKEPROC;
Don't forget to close cursor after finishing working with it.
Version 7.1.4 of PL/SQL Developer doesn't support ref cursor:
SQL> VARIABLE p_cur REFCURSOR;
REFCURSOR not supported
Later versions may support them (in a command window), or you can use SQL*Plus. This is a direct copy-paste from SQL*Plus:
SQL> CREATE OR REPLACE PROCEDURE prc (p_cur OUT SYS_REFCURSOR) IS
2 BEGIN
3 OPEN p_cur FOR SELECT * FROM dual;
4 END;
5 /
Procedure created.
SQL> -- declare variable
SQL> VARIABLE p_cur REFCURSOR;
SQL> BEGIN
2 prc(:p_cur);
3 END;
4 /
PL/SQL procedure successfully completed.
SQL> print p_cur
DUM
---
X

PL/SQL Before-After-Pattern

I would like to use a before-after-pattern approach with PL/SQL (PSEUDO CODE):
Pattern method:
procedure doIt(DO_SOMETHING)
is
l_cnt pls_integer := 1;
begin
loop
begin
DO_SOMETHING;
exit;
exception
when exception changed then
if l_cnt = 2 then
--raise exception...
else
l_cnt := l_cnt + 1;
end if;
end;
end loop;
end;
And execute it like this:
begin
doIt(execute immediate sql_statement using in or out);
end;
As you can see, I would like to use different dynamic sql statements (execute immediate with one or more in and out variables) but always the same before-after-pattern approach.
Have someone an idea how I can solve this problem?
This solution is not so nice, but it works. Perhaps you can start from it and make something better:
I used 2 parameters in the doIt function-
1) the command to execute immediate
2) the arguments as an anydata type
In the execute immediate command I've put all the logic from translating the anydata to some type which I created to wrap the IN OUT parameters.
here is the code:
a type like this should be created for every different command:
create or replace type some_type as object(a number, b number);
/
this is the procedure :
create or replace procedure doIt(aa in varchar2, param IN OUT anydata) is
begin
execute immediate aa using in out param;
end doIt;
/
and this is how I call it (in this example I just selected count(*) from dual into some OUT param):
declare
i number;
prm some_type;
ad anydata;
a number;
b number;
begin
prm := new some_type(a,b);
ad := anydata.convertobject(prm);
doIt('declare prmAd anydata := :0; prm1 some_type; x number; begin x := prmAd.getobject(prm1); select count(*) into prm1.a from dual; :0 := anydata.convertobject(prm1); end;', ad);
i := ad.GetObject(prm);
dbms_output.put_line(prm.a);
end;
basically you can add to the doIt procedure what ever you want, and run with it any command.
I guess you can make things nices- move some of the execute immediate string to the doIt procedure, maybe declare the type better and so on.
I can't find much information on Before/After pattern but looking at what your trying to achieve it could look something like this:
create or replace
procedure doIt(stmt in varchar2, param in varchar2)
is
l_cnt pls_integer := 1;
begin
loop
begin
execute immediate stmt using param;
exit;
exception
when others then
if l_cnt = 2 then
raise;
else
l_cnt := l_cnt + 1;
end if;
end;
end loop;
end;
/
-- run it
exec doIt('insert into my_table (col1) values ( :val1 )', 'richard' );
The problem here is that if you want to pass two parameters then you will have to override 'doIt' eg:
procedure doIt(stmt in varchar2, param1 in varchar2, param2 in varchar2)
...
using param1, param2
Also the other caveat is passing different data types into param. Again you could override the procedure with the correct data type - be warned this could get messy with multiple data types or number of arguments.

Resources