Procedure with refcursor .refcursor should be out parameter - plsql

Please help me writing a program procedure with refcursor. Refcursor should be out parameter.
If i give input deptno it shows employee details in that department,also write unit testing block :
I tried like:
CREATE OR REPLACE PROCEDURE poph (i IN NUMBER, p OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p FOR 'select * from emp where deptno=' || i;
FOR i IN p
LOOP
DBMS_OUTPUT.put_line (i.ename || '**' || i.JOB);
END LOOP;
CLOSE p;
END;
/
///////unit testing/////
DECLARE
M NUMBER := '&n';
n SYS_REFCURSOR;
BEGIN
poph (M, n);
END;

You are making 2 mistakes.
1) Openin a refcursor for dynamic select statement which will not work without execute immediate statement.
OPEN p FOR 'select * from emp where deptno=' || i;
2) Looping through a ref cursor which is not valid;
FOR i IN p
Do it this way:
CREATE OR REPLACE PROCEDURE poph (i IN NUMBER, p OUT SYS_REFCURSOR)
IS
v_sql varchar2(100);
BEGIN
v_sql := 'select employee_id,salary from employee where employee_id=' || i;
execute immediate v_sql;
OPEN p FOR v_sql;
END;
/
///////unit testing/////
DECLARE
M NUMBER := '&n';
n SYS_REFCURSOR;
v_emp_id NUMBER;
v_sal NUMBER;
BEGIN
poph (M, n);
LOOP
FETCH n INTO v_emp_id, v_sal;
EXIT WHEN n%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (v_emp_id);
END LOOP;
END;

Related

Collections not working in PLSQL

By below code is not working for cursor on PLSQL.
I receive an error message.
declare
type abc is varray(10) of number;
cursor x is select Empno from emp where rownum <10;
a abc;
counter number :=1;
begin
a:=abc();
for i in x
loop
a.extend();
a(i):=counter.Empno;
DBMS_output.put_line(a(i));
counter:= end loop;
end;
You have used counter instead of for loop iterator i:
Try;
declare
type abc is varray(10) of number;
cursor x is select Empno from emp where rownum <10;
a abc;
counter number := 1;
begin
a:=abc();
for i in x loop
a.extend();
a(counter) := i.Empno;
DBMS_output.put_line(a(counter));
counter := counter + 1;
end loop;
end;
Or you can use Bulk collect to insert data into varray
declare
type abc is varray(10) of number;
a abc := abc();
begin
select Empno BULK COLLECT INTO a from emp where rownum <10;
for i in 1 .. a.count loop
DBMS_output.put_line(a(i));
end loop;
end;

Inserting data into Global temporary table by executing Stored Procedure in SQLPLUS

I creatd a stored procedure. And a global temporary table inside it.
Now i am inserting data into this table by fatching cursor into local variables.
create or replace PROCEDURE "DEMO"
(
PARM_YEAR IN NUMBER,
PARM_PERIOD IN NUMBER,
PARM_PERIOD_TYPE IN CHAR,
PARM_PERIOD_ROLL IN CHAR,
PARM_TYPE IN VARCHAR2,
P_CURSOR IN OUT types.cursorType
)
IS
LOC_EXISTS NUMBER;
LOC_TYPE_PERIOD CHAR(2);
LOC_CURSOR_YEAR INTEGER;
LOC_CURSOR_PERIOD INTEGER;
LOC_CURSOR_TYPE_PERIOD CHAR(2 BYTE);
LOC_CURSOR_DEV_COPCL NUMBER(10,1);
LOC_CURSOR_DIST_GRP NUMBER(3,0);
CURSOR DIST_CHART IS
SELECT X.YEAR , X.PERIOD, X.TYPE_PERIOD, X.COST AS DEV_COPCL
FROM
SMY_PRVDR_TYPE_PRVDR X;
BEGIN
/* Set Period type */
IF (PARM_PERIOD_TYPE = 'Q' AND PARM_PERIOD_ROLL = 'A') THEN
LOC_TYPE_PERIOD := 'Q';
ELSE IF (PARM_PERIOD_TYPE = 'Q' AND PARM_PERIOD_ROLL = 'R') THEN
LOC_TYPE_PERIOD := 'RQ';
ELSE IF (PARM_PERIOD_TYPE = 'M' AND PARM_PERIOD_ROLL = 'A') THEN
LOC_TYPE_PERIOD := 'M';
ELSE
LOC_TYPE_PERIOD := 'RM';
END IF;
END IF;
END IF;
LOC_EXISTS := 0;
SELECT 1 INTO LOC_EXISTS
FROM ALL_TABLES
WHERE TABLE_NAME LIKE '%DEMO1%';
IF LOC_EXISTS = 1 THEN
EXECUTE IMMEDIATE 'TRUNCATE TABLE DEMO1';
END IF;
OPEN DIST_CHART;
LOOP
FETCH DIST_CHART INTO LOC_CURSOR_YEAR, LOC_CURSOR_PERIOD, LOC_CURSOR_TYPE_PERIOD,
LOC_CURSOR_DEV_COPCL;
EXIT WHEN DIST_CHART%NOTFOUND;
SELECT DIST_GRP(LOC_CURSOR_DEV_COPCL) INTO LOC_CURSOR_DIST_GRP FROM DUAL;
EXECUTE IMMEDIATE 'INSERT INTO DEMO1 VALUES ('|| LOC_CURSOR_YEAR ||', '''||
LOC_CURSOR_PERIOD || ''', '''|| LOC_CURSOR_TYPE_PERIOD ||''', '|| LOC_CURSOR_DEV_COPCL
||', '|| LOC_CURSOR_DIST_GRP || ')';
END LOOP;
CLOSE DIST_CHART;
EXCEPTION
WHEN NO_DATA_FOUND THEN
LOC_EXISTS:=0;
END;
Problem is that..
When i am executing this stored procedure into sql developer data is inserted successfuly into the global temporary table.
But
When i run same execute command into SQLPLUS, procedure run successfully, but data is not inserted into the global temporary table.
Code of GTT is
CREATE GLOBAL TEMPORARY TABLE "ICUSER"."DEMO1"
( "YEAR" NUMBER(4,0),
"PERIOD" NUMBER(2,0),
"TYPE_PERIOD" CHAR(2 BYTE),
"DEV_COPCL" NUMBER(*,1),
"DIST_GRP" NUMBER(3,0)
) ON COMMIT PRESERVE ROWS ;

while executing the function when I'm providing i/p [parameter which is not in table I'm getting error; otherwise it gets executed]

create or replace function get_ware_house_master(p_WAREHOUSE_IDS in varchar2)
return id_wh_id
is
l_warehouse_list id_wh.id := id_wh_id();
str varchar2(300);
begin
str := 'SELECT BM(w.wh_id)
FROM pod_place_warehouse_mapping_tb W
where ( W.wh_id IN (' ||p_WAREHOUSE_IDS || '))';
execute immediate str bulk collect into l_warehouse_list;
for i in l_warehouse_list.first..l_warehouse_list.last loop
dbms_output.put_line(l_warehouse_list(i).wh_id);
end loop;
return l_warehouse_list;
end get_ware_house_master;
How to handle the data which are not present in the table I need to use if l_warehouse_list = null then dbms_output('No DATA FOUND') but if data is not present in the table mentioned in the function I get below error:
numeric or value change error
You could use the count() function on that collection of yours:
create or replace function get_ware_house_master(p_WAREHOUSE_IDS in varchar2)
return id_wh_id
is
l_warehouse_list id_wh.id := id_wh_id();
str varchar2(300);
begin
str := 'SELECT BM(w.wh_id)
FROM pod_place_warehouse_mapping_tb W
where ( W.wh_id IN (' ||p_WAREHOUSE_IDS || '))';
execute immediate str bulk collect into l_warehouse_list;
if l_warehouse_list.count() >0 then
for i in l_warehouse_list.first..l_warehouse_list.last loop
dbms_output.put_line(l_warehouse_list(i).wh_id);
end loop;
else
dbms_output.put_line('No data');
end if;
return l_warehouse_list;
end get_ware_house_master;
Here is how to illustrate the latest with little simplification to your code:
CREATE OR REPLACE TYPE id_wh_id AS TABLE OF VARCHAR2(100);
create or replace function get_ware_house_master(p_WAREHOUSE_IDS in varchar2)
return id_wh_id
is
l_warehouse_list id_wh_id := id_wh_id();
str varchar2(300);
begin
str := 'SELECT * from dual where ''a''=''' ||p_WAREHOUSE_IDS || '''';
dbms_output.put_line(str);
execute immediate str bulk collect into l_warehouse_list;
if l_warehouse_list.count() >0 then
for i in l_warehouse_list.first..l_warehouse_list.last loop
dbms_output.put_line(l_warehouse_list(i));
end loop;
else
dbms_output.put_line('No data');
end if;
return l_warehouse_list;
end get_ware_house_master;
/
-- and call to the function:
select get_ware_house_master('sbi') from dual
union all select get_ware_house_master('a') from dual
;

Dynamic String contcatination in Cursor

I have a function which concatinates the value for a cursor. Its now only concatinates 4 columns and that column name should be hardcoded. Is there a way to have a generic solution for this, such that if i pass a cursor it will automatically concatinate data regardless of column name and number of columns in 11g.
FUNCTION generateData(p_dataCursor IN SYS_REFCURSOR)
RETURN VARCHAR2 AS
-- ---------------------------------------------------------------------
crlf VARCHAR2(2) := chr(13)||chr(10);
lv_message VARCHAR2(32000);
BEGIN
FOR rec IN p_dataCursor
LOOP
lv_message := lv_message || rec.a||','||rec.b||','||rec.c||','||rec.d || crlf;
END LOOP;
RETURN lv_message;
END;
Since 11g Oracle built-in package DBMS_SQL provides function TO_CURSOR_NUMBER - "This function takes an OPENed strongly or weakly-typed ref cursor and transforms it into a DBMS_SQL cursor number."
Example code:
DECLARE
l_cursor SYS_REFCURSOR;
FUNCTION generateData(p_dataCursor IN SYS_REFCURSOR)
RETURN VARCHAR2 AS
curs SYS_REFCURSOR := p_dataCursor;
l_cursorid NUMBER;
l_column_count INTEGER;
l_describe_table DBMS_SQL.DESC_TAB;
l_numvar NUMBER;
l_ignore INTEGER;
l_value VARCHAR2(2000);
l_coma VARCHAR2(10);
crlf VARCHAR2(2) := chr(13)||chr(10);
lv_message VARCHAR2(32000);
BEGIN
l_cursorid := dbms_sql.to_cursor_number( curs );
dbms_sql.describe_columns( l_cursorid, l_column_count, l_describe_table );
FOR i IN 1..l_column_count LOOP
dbms_sql.define_column(l_cursorid, i, l_value, 2000);
END LOOP;
LOOP
IF DBMS_SQL.FETCH_ROWS(l_cursorid)>0 THEN
l_coma := '';
FOR i IN 1..l_column_count LOOP
dbms_sql.column_value(l_cursorid, i, l_value);
lv_message := lv_message || l_coma || l_value;
l_coma := ',';
END LOOP;
lv_message := lv_message || crlf;
ELSE
EXIT;
END IF;
END LOOP;
dbms_sql.close_cursor( l_cursorid );
RETURN lv_message;
END;
BEGIN
open l_cursor FOR 'SELECT 1 as A, 2 AS B, 3 AS C, 4 AS D FROM DUAL UNION ALL SELECT 1 as A, 2 AS B, 3 AS C, 4 AS D FROM DUAL';
dbms_Output.put_Line(generateData(l_cursor));
END;
/

Function returns a table

Here I have a function which takes 'COMMA SEPARATED STRING' and returns a pipelined table.
create or replace
FUNCTION parse_comma_delimited
(
iv_list IN VARCHAR2,
v_delimiter IN VARCHAR2 DEFAULT ','
)
RETURN parse_comma_delimited_pkg.tt_v_tablevalues_type PIPELINED
AS
v_list VARCHAR2(8000) := iv_list;
v_item VARCHAR2(255);
v_temp SYS_REFCURSOR;
v_temp_1 TT_V_TABLEVALUES%ROWTYPE;
BEGIN
WHILE ( LENGTHB(v_list) > 0 )
LOOP
BEGIN
IF INSTR(v_list, v_delimiter) > 0 THEN
BEGIN
v_item := SUBSTR(v_list, 1, (INSTR(v_list, v_delimiter) - 1)) ;
v_list := SUBSTR(v_list, (INSTR(v_list, v_delimiter)
|| LENGTHB(v_delimiter)), LENGTHB(v_list)) ;
END;
ELSE
BEGIN
v_item := v_list ;
v_list := NULL ;
END;
END IF;
INSERT INTO tt_v_tablevalues ( item ) VALUES (v_item);
END;
END LOOP;
OPEN v_temp FOR
SELECT *
FROM tt_v_tablevalues;
LOOP
FETCH v_temp INTO v_temp_1;
EXIT WHEN v_temp%NOTFOUND;
PIPE ROW ( v_temp_1 );
END LOOP;
END;
But when I call the function its retuning only one row.
INSERT INTO SRC_PK_INSERT (pk_key)
SELECT *
FROM TABLE(parse_comma_delimited((
SELECT *
FROM SRC_PK_INSERT
WHERE RULE_NAME = 'RULES'
)));
With a pipelined function you populate the row_type, then call pipe row.
See the documentation here: http://docs.oracle.com/cd/B28359_01/appdev.111/b28425/pipe_paral_tbl.htm#CHDJEGHC
Example from docs:
CREATE FUNCTION StockPivot(p refcur_pkg.refcur_t) RETURN TickerTypeSet
PIPELINED IS
out_rec TickerType := TickerType(NULL,NULL,NULL);
in_rec p%ROWTYPE;
BEGIN
LOOP
FETCH p INTO in_rec;
EXIT WHEN p%NOTFOUND;
-- first row
out_rec.ticker := in_rec.Ticker;
out_rec.PriceType := 'O';
out_rec.price := in_rec.OpenPrice;
PIPE ROW(out_rec);
-- second row
out_rec.PriceType := 'C';
out_rec.Price := in_rec.ClosePrice;
PIPE ROW(out_rec);
END LOOP;
CLOSE p;
RETURN;
END;
/

Resources