Dynamic String contcatination in Cursor - plsql

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;
/

Related

Why do I get ORA-06531: reference to uninitialized collection while trying to call procedure?

i am trying to call the procedure my_package.procedure_test like bellow:
declare
v_counter BINARY_INTEGER := 1;
a tbl_familles;
v_mytable tbl_familles;
begin
PK_SOA_Famille_Test.get_famille('zz','zz', v_mytable);
while v_counter <= v_mytable.count
loop
Dbms_Output.Put_Line(v_mytable(v_counter).nom);
v_counter := v_counter + 1;
end loop;
end;
But i get the exception:
ORA-06531: reference to uninitialized collection
The Package definition:
CREATE OR REPLACE Package PK_SOA_Famille_Test as
PROCEDURE get_famille
(num_ass IN VARCHAR2, num_indd IN VARCHAR2,
OutTableParam OUT tbl_familles);
end PK_SOA_Famille_Test;
The Procedure body:
CREATE OR REPLACE PACKAGE BODY PK_SOA_Famille_Test AS
PROCEDURE get_famille
(num_ass IN VARCHAR2, num_indd IN VARCHAR2, OutTableParam OUT tbl_familles) IS
v_counter number := 0;
Cursor C_typ_famille
(
num_ass varchar2 ,num_indd varchar2
) Is
SELECT nom, prenom, num_imm ,num_ind ,nat_int ,dat_naiss, num_cin, der_stat, der_sit, datsit, indpere, indmere
from typ_famille
where (num_imm = num_ass AND num_ind = num_indd) ;
C2 C_typ_famille%ROWTYPE;
BEGIN
IF num_indd is null then
for EmpCursor in
(select nom,prenom,num_imm ,num_ind, nat_int, dat_naiss, num_cin, der_stat, der_sit, datsit, indpere, indmere
from typ_famille
where num_imm = num_ass)
loop
v_counter := v_counter + 1;
OutTableParam(v_counter).nom := EmpCursor.nom;
OutTableParam(v_counter).prenom := EmpCursor.prenom;
OutTableParam(v_counter).num_imm := EmpCursor.num_imm;
OutTableParam(v_counter).num_ind := EmpCursor.num_ind;
OutTableParam(v_counter).nat_int := EmpCursor.nat_int ;
OutTableParam(v_counter).dat_naiss := EmpCursor.dat_naiss;
OutTableParam(v_counter).num_cin := EmpCursor.num_cin;
OutTableParam(v_counter).der_stat := EmpCursor.der_stat;
OutTableParam(v_counter).der_sit := EmpCursor.der_sit;
OutTableParam(v_counter).datsit := EmpCursor.datsit;
OutTableParam(v_counter).indpere := EmpCursor.indpere;
OutTableParam(v_counter).indmere := EmpCursor.indmere;
end loop;
END IF ;
END get_famille;
END PK_SOA_Famille_Test;
/
I have also created the types rec_famille as an object and tbl_familles as a table of rec_famille in schema level.
When num_indd is NOT NULL then PK_SOA_Famille_Test.get_famille effectively does nothing. This leaves OutTableParam uninitialized. You could have an ELSE in your IF-statement with OutTableParam := tbl_familles(); as EJ Egyed suggested.
Additionally, looping through a cursor to fill a collection is a waste: you should use BULK COLLECT instead.
CREATE OR REPLACE PACKAGE BODY pk_soa_famille_test IS
PROCEDURE get_famille(num_ass IN VARCHAR2,
num_indd IN VARCHAR2,
outtableparam OUT) IS
BEGIN
IF num_indd IS NULL THEN
SELECT nom,
prenom,
num_imm,
num_ind,
nat_int,
dat_naiss,
num_cin,
der_stat,
der_sit,
datsit,
indpere,
indmere
BULK COLLECT
INTO outtableparam
FROM typ_famille
WHERE num_imm = num_ass;
ELSE
outtableparam := tbl_familles;
END IF;
END get_famille;
END pk_soa_famille_test;

Procedure with refcursor .refcursor should be out parameter

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;

i have a function which returns multiple o/p i need to call this function to insert in a table

create or replace type branch_warehouse as object (wh_id number,wh_name varchar2(100));
create or replace type id_warehouse_list as table of branch_warehouse;
function get_ware_house_branch(p_BRANCH_IDS in out varchar2)
return id_warehouse_list is
l_warehouse_list id_warehouse_list := id_warehouse_list();
str varchar2(300);
begin
str := 'SELECT BRANCH_WAREHOUSE(w.wh_id, w.wh_name)
FROM POD_WAREHOUSE_MASTER W
where ( W.BRANCH_ID IN (' || p_BRANCH_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 || ', ' || l_warehouse_list(i).wh_name);
end loop;
return l_warehouse_list;
end;
procedure insert_place_warehouse_map(p_PLACE_NAME in varchar2,
p_BRANCH_IDS in number,
p_status out varchar2,
p_status_dtl out varchar2) is
v_ID number;
l_warehouse_list id_warehouse_list := id_warehouse_list();
begin
v_ID := get_place_id (p_PLACE_NAME);
l_warehouse_list := get_ware_house_branch(p_BRANCH_IDS);
for i in l_warehouse_list.first..l_warehouse_list.last loop
insert into pod_place_warehouse_map(
id,
place_id,
wh_id,
wh_name)
values(
pod_unique_val_seq.nextval,
v_ID,
l_warehouse_list(i).wh_id,
l_warehouse_list(i).wh_name);
v_rec_cnt := sql%rowcount;
end loop;
commit;
p_status := 'SUCESS';
p_status_dtl := v_rec_cnt||' Record Inserted Successfully';
exception
when others then
rollback;
p_status_dtl := sqlcode||' - '||substr(sqlerrm,1,100);
p_status := 'ERROR';
end;
i am getting error p_BRANCH_IDS cannot be used as assignment operator in the 2nd code
first code is working,
please help
This is because the parameter is declared as in out in first code, but as in in second.

SYS.DBMS_DDL.WRAP not allowing pragma and intctx

I am using the below procedure to wrap the PL/SQL Code.
declare
l_source DBMS_SQL.VARCHAR2A;
l_wrap DBMS_SQL.VARCHAR2A;
l_wrap1 clob;
typ_ibt utl_file.file_type;
cnt number := 0;
v_directory varchar2(400) := 'd:\ftpedi\eqpm\eqpm_hold\';
cursor cur_name_get is
select distinct name object_name,type object_type
from user_source
where type = 'PROCEDURE'
and name = 'PROCESS_TIME_INSERT';
cursor cut_text_get ( p_type in varchar2 , p_name in varchar2 ) is
select replace(text,chr(10),'') text
from user_source
where type = p_type
and name = p_name;
begin
for i in cur_name_get
loop
l_source.delete;l_wrap.delete;
open cut_text_get ( i.object_type,i.object_name );
fetch cut_text_get bulk collect into l_source;
close cut_text_get;
l_source (1) := 'CREATE OR REPLACE ' || l_source (1);
l_wrap := SYS.DBMS_DDL.WRAP(ddl => l_source,
lb => 1,
ub => l_source.count);
for i in 1..l_wrap.count
loop
if i = 1
then
l_wrap1 := l_wrap(i);
else
l_wrap1 := l_wrap1 || l_wrap(i);
end if;
insert into ibt_global_inter_transfer ( git_process_id,git_c_1)
values ( 3004, l_wrap1 );
end loop;
end loop;
exception when others
then
dbms_output.put_line('sqlerrm '||sqlerrm||dbms_utility.format_error_backtrace);
end;
The above procedures warps the normal procedure, but not allowing the special character like 'PRAGMA'.
The below is the sample procedure, which is not wrapping.
CREATE OR REPLACE
PROCEDURE xml_insert ( p_in_xml in xmltype ,p_status out varchar2,p_message out varchar2) is
intctx DBMS_XMLSTORE.ctxtype;
rows number;
begin
p_status := 'S';
p_message := 'Success';
intctx := Dbms_xmlstore.newcontext('IBT_GLOBAL_INTER_TRANSFER');
dbms_xmlstore.clearupdatecolumnlist(intctx);
dbms_xmlstore.setupdatecolumn(intCtx,'GIT_PROCESS_ID');
dbms_xmlstore.setupdatecolumn(intCtx,'GIT_SESSION_ID');
rows := Dbms_xmlstore.insertxml(intctx,p_in_xml);
dbms_xmlstore.closecontext(intctx);
exception when others
then
p_status := 'R';
p_message := sqlerrm||dbms_utility.format_error_backtrace;
return;
end;
Could anyone help?
Update
(I misunderstood the problem. I thought you meant the wrapped code wasn't created, now I understand the real problem is that the wrapped output does not compile.)
Remove the replace, there needs to be white space between some of the lines.
Replace:
--select replace(text,chr(10),'') text
with:
select text
Procedure:
CREATE OR REPLACE
PROCEDURE xml_insert ( p_in_xml in xmltype ,p_status out varchar2,p_message out varchar2) is
intctx DBMS_XMLSTORE.ctxtype;
rows number;
pragma autonomous_transaction; --ADDED
begin
p_status := 'S';
p_message := 'Success';
intctx := Dbms_xmlstore.newcontext('IBT_GLOBAL_INTER_TRANSFER');
dbms_xmlstore.clearupdatecolumnlist(intctx);
dbms_xmlstore.setupdatecolumn(intCtx,'GIT_PROCESS_ID');
dbms_xmlstore.setupdatecolumn(intCtx,'GIT_SESSION_ID');
rows := Dbms_xmlstore.insertxml(intctx,p_in_xml);
dbms_xmlstore.closecontext(intctx);
exception when others
then
p_status := 'R';
p_message := sqlerrm||dbms_utility.format_error_backtrace;
return;
end;
/
PL/SQL Block wrapping code:
declare
l_source DBMS_SQL.VARCHAR2A;
l_wrap DBMS_SQL.VARCHAR2A;
l_wrap1 clob;
typ_ibt utl_file.file_type;
cnt number := 0;
v_directory varchar2(400) := 'd:\ftpedi\eqpm\eqpm_hold\';
cursor cur_name_get is
select distinct name object_name,type object_type
from user_source
where type = 'PROCEDURE'
and name = 'XML_INSERT'; --CHANGED
cursor cut_text_get ( p_type in varchar2 , p_name in varchar2 ) is
--select replace(text,chr(10),'') text --WOOPS!
select text
from user_source
where type = p_type
and name = p_name;
begin
for i in cur_name_get
loop
l_source.delete;l_wrap.delete;
open cut_text_get ( i.object_type,i.object_name );
fetch cut_text_get bulk collect into l_source;
close cut_text_get;
l_source (1) := 'CREATE OR REPLACE ' || l_source (1);
l_wrap := SYS.DBMS_DDL.WRAP(ddl => l_source,
lb => 1,
ub => l_source.count);
for i in 1..l_wrap.count
loop
if i = 1
then
l_wrap1 := l_wrap(i);
else
l_wrap1 := l_wrap1 || l_wrap(i);
end if;
--insert into ibt_global_inter_transfer ( git_process_id,git_c_1)
--values ( 3004, l_wrap1 );
dbms_output.put_line(l_wrap1);
end loop;
end loop;
exception when others
then
dbms_output.put_line('sqlerrm '||sqlerrm||dbms_utility.format_error_backtrace);
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