I have bulk collected data in T_USERRECORD through Cursor. Now inside T_USERRECORD loop I have to populate a separate collection for insert and update according to the commented section below, and have to make a bulk insert and update outside the loop. I am getting error(Reference to uninitialized collection) in this.
CREATE OR REPLACE PACKAGE PKG_USERRECORD IS
PROCEDURE PR_CREATE_USERRECORD_STRING(PC_STATUS OUT VARCHAR2,
PC_MESSAGE OUT VARCHAR2);
END PKG_USERRECORD;
/
CREATE OR REPLACE PACKAGE BODY PKG_USERRECORD IS
CONST_SUCCESS CONSTANT CHAR(1) := 'S';
CONST_FAILURE CONSTANT CHAR(1) := 'F';
PROCEDURE PR_CREATE_USERRECORD_STRING(PC_USER_INPUT IN VARCHAR2,
PC_STATUS OUT VARCHAR2,PC_MESSAGE OUT VARCHAR2) IS
CURSOR CUR_USERSTRING IS
SELECT REGEXP_SUBSTR(USER_INPUT, '([^~]*)(~|$)', 1, 1, NULL, 1) USERID,
REGEXP_SUBSTR(USER_INPUT, '([^~]*)(~|$)', 1, 2, NULL, 1) USERNAME,
REGEXP_SUBSTR(USER_INPUT, '([^~]*)(~|$)', 1, 3, NULL, 1) DATE_OF_JOINING,
REGEXP_SUBSTR(USER_INPUT, '([^~]*)(~|$)', 1, 4, NULL, 1) CREATED_BY,
REGEXP_SUBSTR(USER_INPUT, '([^~]*)(~|$)', 1, 5, NULL, 1) CREATION_DATE,
REGEXP_SUBSTR(USER_INPUT, '([^~]*)(~|$)', 1, 6, NULL, 1) MODIFIED_BY,
REGEXP_SUBSTR(USER_INPUT, '([^~]*)(~|$)', 1, 7, NULL, 1) MODIFIED_DATE
FROM (
SELECT REGEXP_SUBSTR('1~AKASH~01-AUG-22~5~01-AUG-22~4~04-AUG-22^2~AJAY~02-AUG-22~6~02-AUG-22~4~04-AUG-22^3~MEGHA~02-AUG-22~6~02-AUG-22~4~04-AUG-22', '[^^]+', 1, LEVEL) USER_INPUT
FROM DUAL
CONNECT BY LEVEL <= LENGTH('1~AKASH~01-AUG-22~5~01-AUG-22~4~04-AUG-22^2~AJAY~02-AUG-22~6~02-AUG-22~4~04-AUG-22^3~MEGHA~02-AUG-22~6~02-AUG-22~4~04-AUG-22') -
LENGTH(REPLACE('1~AKASH~01-AUG-22~5~01-AUG-22~4~04-AUG-22^2~AJAY~02-AUG-22~6~02-AUG-22~4~04-AUG-22^3~MEGHA~02-AUG-22~6~02-AUG-22~4~04-AUG-22', '^')) + 1
);
TYPE T_CUR_USERSTRING IS TABLE OF CUR_USERSTRING%ROWTYPE;
T_USERRECORD T_CUR_USERSTRING;
T_USERRECORD_INSERT T_CUR_USERSTRING;--Have to populate bulk collection for insert inside T_USERRECORD(it is giving error, collection not initialised)
T_USERRECORD_UPDATE T_CUR_USERSTRING;--Have to populate bulk collection for update inside T_USERRECORD(it is giving error, collection not initialised)
BEGIN
OPEN CUR_USERSTRING;
LOOP
FETCH CUR_USERSTRING BULK COLLECT
INTO T_USERRECORD;
IF T_USERRECORD.COUNT = 0 THEN
EXIT;
END IF;
FOR I IN T_USERRECORD.FIRST .. T_USERRECORD.LAST LOOP
-- A. IF THIS IS A NEW RECORD - POPULATE A SEPARATE COLLECTION FOR INSERTS
--B. IF THIS IS AN EXISTING RECORD - POPULATE A SEPARATE COLLECTION FOR UPDATES AND ONLY UPDATE THE CHANGED RECORDS NOT THE WHOLE UPDATE
--C. DELETION
--D. AUDIT TRAIL
IF T_USERRECORD(I).USERID IS NULL THEN
FETCH CUR_USERSTRING BULK COLLECT
INTO T_USERRECORD_INSERT;
ELSE
FETCH CUR_USERSTRING BULK COLLECT
INTO T_USERRECORD_UPDATE;
END IF;
END LOOP;
FORALL N IN T_USERRECORD_INSERT.FIRST .. T_USERRECORD_INSERT.LAST
INSERT INTO QM_USERDATA VALUES T_USERRECORD_INSERT (N);
FORALL M IN T_USERRECORD_UPDATE.FIRST .. T_USERRECORD_UPDATE.LAST
--ONLY UPDATE THE CHANGED RECORDS NOT THE WHOLE UPDATE
UPDATE QM_USERDATA
SET USERNAME = T_USERRECORD_UPDATE(M).USERNAME,
DATE_OF_JOINING = T_USERRECORD_UPDATE(M).DATE_OF_JOINING,
CREATED_BY = T_USERRECORD_UPDATE(M).CREATED_BY,
CREATION_DATE = T_USERRECORD_UPDATE(M).CREATION_DATE,
MODIFIED_BY = T_USERRECORD_UPDATE(M).MODIFIED_BY,
MODIFIED_DATE = T_USERRECORD_UPDATE(M).MODIFIED_DATE
WHERE USERID = T_USERRECORD_UPDATE(M).USERID;
END LOOP;
CLOSE CUR_USERSTRING;
COMMIT;
PC_STATUS := CONST_SUCCESS;
EXCEPTION
WHEN OTHERS THEN
CLOSE CUR_USERSTRING;
PC_STATUS := CONST_FAILURE;
PC_MESSAGE := 'ERROR IN FETCHING LIST' || ' ' || SQLCODE || ' ' ||
SQLERRM || 'PR_CREATE_USERRECORD_STRING';
END PR_CREATE_USERRECORD_STRING;
END PKG_USERRECORD;
/
--Use to display data
DECLARE
V_STRING VARCHAR2(5000);
V_STATUS CHAR(1);
V_MESSAGE VARCHAR2(5000);
BEGIN
PKG_USERRECORD.PR_CREATE_USERRECORD_STRING
('1~AKASH~01-AUG-22~5~01-AUG-22~4~04-AUG-22^2~AJAY~02-AUG-22~6~02-AUG-22~4~04-AUG-22^3~MEGHA~02-AUG-22~6~02-AUG-22~4~04-AUG-22',V_STATUS,V_MESSAGE);
DBMS_OUTPUT.PUT_LINE(V_STATUS||' '||V_MESSAGE);
END;
As the error says, you need to initialise your collections - for insert and update, not necessary for the main collection - either as they are declared:
...
T_USERRECORD_INSERT T_CUR_USERSTRING := T_CUR_USERSTRING();
T_USERRECORD_UPDATE T_CUR_USERSTRING := T_CUR_USERSTRING();
BEGIN
...
or in the main body:
...
T_USERRECORD_INSERT T_CUR_USERSTRING;
T_USERRECORD_UPDATE T_CUR_USERSTRING;
BEGIN
-- initialiase insert/update collections (could be done in declaration too)
T_USERRECORD_INSERT := T_CUR_USERSTRING();
T_USERRECORD_UPDATE := T_CUR_USERSTRING();
...
Then when you have decided whether to handle as an insert or update, do not delete all the existing entries from the relevant collection, and don't re-fetch into those collections - now you have removed the limit the fetch won't find anything, and if the limit was there it would fetch all the records after the one you are looking at. Instead, copy the current record to the collection:
IF T_USERRECORD(I).USERID IS NULL THEN
T_USERRECORD_INSERT.EXTEND;
T_USERRECORD_INSERT(T_USERRECORD_INSERT.COUNT) := T_USERRECORD(I);
ELSE
T_USERRECORD_UPDATE.EXTEND;
T_USERRECORD_UPDATE(T_USERRECORD_UPDATE.COUNT) := T_USERRECORD(I);
END IF;
db<>fiddle with some additional comments, matching spec and body procedure declaration, dummy table, and using the passed-in value for the regex split; and showing S for both an update and an insert scenario.
There are other areas you could look at, including whether you really need to PL/SQL and loops/collections etc. for this at all - it could be done with a single MERGE - but that's rather beyond the scope of your current issue.
Related
I want to use FORALL to insert data into a table. But, in my below code I will not be able to
get l_final_amt and l_reference_number variables outside the FOR loop of l_tbl_table_test_retrieve.
How to use FORALL to insert data into a table when values are not in the given type?
CREATE OR REPLACE PACKAGE test_FORALL AS
PROCEDURE pr_test_FORALL;
END test_FORALL;
CREATE OR REPLACE PACKAGE BODY test_FORALL AS
PROCEDURE pr_test_FORALL IS
TYPE ty_tbl_table_test IS TABLE OF table_test%ROWTYPE INDEX BY BINARY_INTEGER;
l_tbl_table_test_retrieve ty_tbl_table_test;
l_tbl_table_test ty_tbl_table_test;
l_final_amt INTEGER;
l_reference_number VARCHAR2(100);
BEGIN
SELECT * BULK COLLECT
INTO l_tbl_table_test_retrieve
FROM table_test t1;
FOR i IN 1 .. l_tbl_table_test_retrieve.COUNT
LOOP
l_tbl_table_test(l_tbl_table_test.COUNT + 1) := l_tbl_table_test_retrieve(i);
l_final_amt := l_final_amt + 10;
l_reference_number := SYSDATE + l_tbl_table_test_retrieve(i).ID;
insert into some_other_table(fname, address,final_amt,ref_number)
values(l_tbl_table_test_retrieve(i).fname, l_tbl_table_test_retrieve(i).address,l_final_amt,l_reference_number);
END LOOP;
--I want to insert into some_other_table using FORALL. But,l_final_amt and l_reference_number variables
-- are not available in l_tbl_table_test_retrieve.
EXCEPTION
DBMS_OUTPUT.put_line('EXCEPTION occurred');
END;
END pr_test_FORALL;
END test_FORALL;
Use a cursor and add the fields into the rows returned by the cursor:
PROCEDURE pr_test_FORALL IS
DECLARE csrData AS CURSOR FOR
SELECT t1.*,
NULL AS COUNT_VAL,
NULL AS FINAL_AMT,
NULL AS REFERENCE_NUMBER
FROM TABLE_TEST t1;
TYPE ty_tbl_table_test IS
TABLE OF csrData%ROWTYPE -- Note: csrData%ROWTYPE
INDEX BY BINARY_INTEGER;
l_tbl ty_tbl_table_test;
l_final_amt INTEGER := 0;
l_reference_number VARCHAR2(100);
BEGIN
OPEN csrData
FETCH csrData
BULK COLLECT INTO l_tbl;
CLOSE csrData;
FOR i IN 1 .. l_tbl.COUNT LOOP
l_final_amt := l_final_amt + 10;
l_tbl(i).FINAL_AMT := l_final_amt;
l_tbl(i).REFERENCE_NUMBER := SYSDATE + l_tbl(i).ID;
END LOOP;
FORALL i IN l_tbl.FIRST..l_tbl.LAST
INSERT INTO SOME_OTHER_TABLE
(FNAME, ADDRESS, FINAL_AMT, REF_NUMBER)
VALUES
(l_tbl(i).FNAME,
l_tbl(i).ADDRESS,
l_tbl(i).FINAL_AMT,
l_tbl(i).REFERENCE_NUMBER);
EXCEPTION
DBMS_OUTPUT.put_line('EXCEPTION occurred');
END pr_test_FORALL;
You could convert the whole thing into two inserts of the below form into the required tables.
I see that in your code l_reference_number is defined as a VARCHAR2 variable but it sounds like a number. ( SYSDATE + some_number ) will yield a date type. It will be implicitly converted into a string based on your NLS_ settings when you assign it to a varchar2. I'm not sure what do you want to store in there as a "REFERENCE_NUMBER".
INSERT INTO some_other_table (
fname,
address,
final_amt,
ref_number
)
SELECT fname,
address,
10 * ROWNUM AS final_amt,
SYSDATE + id as reference_number
FROM table_test;
I'm inserting lots of rows into a table and some of the columns are blank for some of the rows.
How can I skip insert if some important fields are blank?
for example there is a table 'people' and my important fields are name,cityName and age.
1 INSERT INTO people VALUES('customerid1','name', 'cityName', 50, 'anotherValue')
2 INSERT INTO people VALUES('customerid2','', '', '' , 'anotherValue')
3 INSERT INTO people VALUES('customerid3','name', 'cityName', 20, 'anotherValue')
4 INSERT INTO people VALUES('customerid4','name', 'cityName', 19, 'anotherValue')
here 2nd row name,cityName and age are blank.
if those three fields are blank then dont insert that row.this is just an example i have more fields to check so need to avoid 'if condition' to check blank or not.
another example
FUNCTION TEST_FUN (increment_i in VARCHAR2, increment_j IN VARCHAR2,mod_id IN VARCHAR2 )
RETURN numeric IS
j_val VARCHAR2(100);
i_val VARCHAR2(100);
BEGIN
i_val := increment_i;
j_val := increment_j;
IF mod_id != 'loop' THEN
j_val := i_val;
END IF;
INSERT
INTO TEST.testpartytable
(
reffer_id,
customer_id,
customer_joint,
fullname,
nature,
counter_bus,
country,
status
)
VALUES
(
REFFER_ID_AR,
CUSTOMER_ID_ARR(i_val),
CUSTOMER_JOINT,
LEGALNAME_KBC_ARR(i_val),
NATURERE_KBC_ARR(j_val),
COUNTERBUSACT_KBC_ARR(j_val),
COUNTRY_KBC_ARR(j_val),
STATUS
);
return i_val;
END TEST_FUN ;
skip insert if 'fullname,nature,counter_bus,country' fields are blank .Datas coming from colletion.
Any help is appreciated.
Thanks!
well you can check first if the values are null or not null:
declare CHECK_VAL varchar2(100);
BEGIN
select CUSTOMER_ID_ARR(i_val) into CHECK_VAL from dual;
if(i_val is not null) then
insert...
end if;
END;
You can alternavely make the column to not null, and raise exeception when you get error for a value not null.
You can apply a NOT NULL constraint to your important columns so that when any of them assigned with a NULL value an error will be raised, specifically ORA -1400 (cannot insert null) exception. You can then catch this exception in your program and just do nothing when this exception is raised.
Sample below,
CREATE TABLE TEST_TABLE
(col1 NUMBER NOT NULL, col2 NUMBER NOT NULL);
DECLARE
CANNOT_INSERT_NULL EXCEPTION;
PRAGMA EXCEPTION_INIT(cannot_insert_null, -1400);
num NUMBER;
BEGIN
FOR i IN 1..10 LOOP
num := 2;
IF i BETWEEN 3 AND 5 THEN
num := NULL; //since this is null, insert statement below will error and handled and won't be inserted
END IF;
BEGIN
INSERt INTO test_table
(col1,col2)
VALUES
(i, num);
EXCEPTION
WHEN CANNOT_INSERT_NULL THEN
NULL;
END;
END LOOP;
END;
/
SELECT *
FROM test_table;
Whenever the length of string l_long_string is above 4000 characters, the following code is throwing an error:
ORA-01460: unimplemented or unreasonable conversion requested
Instead of the nested regexp_substr query, when I try to use
SELECT column_value
FROM TABLE(l_string_coll)
it throws:
ORA-22905: cannot access rows from a non-nested table item
How can I modify the dynamic query?
Notes:
- l_string_coll is of type DBMS_SQL.VARCHAR2S, and comes as input to my procedure (here, i have just shown as an anonymous block)
- I'll have to manage without creating a User-defined Type in DB schema, so I am using the in-built DBMS_SQL.VARCHAR2S.
- This is not the actual business procedure, but is close to this. (Can't post the original)
- Dynamic query has to be there since I am using it for building the actual query with session, current application schema name etc.
/*
CREATE TABLE some_other_table
(word_id NUMBER(10), word_code VARCHAR2(30), word VARCHAR2(255));
INSERT INTO some_other_table VALUES (1, 'A', 'AB');
INSERT INTO some_other_table VALUES (2, 'B', 'BC');
INSERT INTO some_other_table VALUES (3, 'C', 'CD');
INSERT INTO some_other_table VALUES (4, 'D', 'DE');
COMMIT;
*/
DECLARE
l_word_count NUMBER(10) := 0;
l_counter NUMBER(10) := 0;
l_long_string VARCHAR2(30000) := NULL;
l_dyn_query VARCHAR2(30000) := NULL;
l_string_coll DBMS_SQL.VARCHAR2S;
BEGIN
-- l_string_coll of type DBMS_SQL.VARCHAR2S comes as Input to the procedure
FOR i IN 1 .. 4100
LOOP
l_counter := l_counter + 1;
l_string_coll(l_counter) := 'AB';
END LOOP;
-- Above input collection is concatenated into CSV string
FOR i IN l_string_coll.FIRST .. l_string_coll.LAST
LOOP
l_long_string := l_long_string || l_string_coll(i) || ', ';
END LOOP;
l_long_string := TRIM(',' FROM TRIM(l_long_string));
dbms_output.put_line('Length of l_long_string = ' || LENGTH(l_long_string));
/*
Some other tasks in PLSQL done successfully using the concatenated string l_long_string
*/
l_dyn_query := ' SELECT COUNT(*)
FROM some_other_table
WHERE word IN ( SELECT TRIM(REGEXP_SUBSTR(str, ''[^,]+'', 1, LEVEL)) word
FROM ( SELECT :string str FROM SYS.DUAL )
CONNECT BY TRIM(REGEXP_SUBSTR(str, ''[^,]+'', 1, LEVEL)) IS NOT NULL )';
--WHERE word IN ( SELECT column_value FROM TABLE(l_string_coll) )';
EXECUTE IMMEDIATE l_dyn_query INTO l_word_count USING l_long_string;
dbms_output.put_line('Word Count = ' || l_word_count);
EXCEPTION
WHEN OTHERS
THEN
dbms_output.put_line('SQLERRM = ' || SQLERRM);
dbms_output.put_line('FORMAT_ERROR_BAKCTRACE = ' || dbms_utility.format_error_backtrace);
END;
/
How can I modify the dynamic query?
First of all. Based on the code you've provided, there is absolutely no need to use dynamic, native or DBMS_SQL dynamic SQL at all.
Secondly, SQL cannot operate on "strings" that are greater than 4K bytes in length(Oracle versions prior to 12c), or 32K bytes(Oracle version 12cR1 and up, if MAX_STRING_SIZE initialization parameter is set to EXTENDED).
PL/SQL, on the other hand, allows you to work with varchar2() character strings that are greater than 4K bytes (up to 32Kb) in length. If you just need to count words in a comma separated sting, you can simply use regexp_count() regular expression function(Oracle 11gr1 and up) as follows:
set serveroutput on;
set feedback off;
clear screen;
declare
l_str varchar2(100) := 'aaa,bb,ccc,yyy';
l_numOfWords number;
begin
l_numOfWords := regexp_count(l_str, '[^,]+');
dbms_output.put('Number of words: ');
dbms_output.put_line(to_char(l_numOfWords));
end;
Result:
Number of words: 4
Dear Oracle Developers,
I have searched and googled to find a solution for my problem but nothing helped me.
Situation :
TABLE : CUSTOMER(....);
My problem is : I want to create a stored procedure say get_customers to return an array of customer rows. I have no idea how to get this working.
I tried to create a type customer_rec and using a cursor to retrieve no more than maxRows
create or replace procedure get_customers(maxRows IN NUMBER, ??? OUT ????)
How to define the OUT parameter ?
How to retrieve the rows in the array using a cursor ?
Thanks a lot
I would like to answer this in a way that discourages passing around arrays, when passing around cursors is a more sound approach. It doesn't exactly answer the question as posed, but it is an answer. Thinking cursors instead of thinking arrays is more efficient and thus more scalable. Also, it can be much easier code to maintain.
create table customer (
customer_id number(2) primary key,
customer_name varchar2(200) );
insert into customer values (1, 'Customer One');
insert into customer values (2, 'Customer Two');
insert into customer values (3, 'Customer Three');
insert into customer values (4, 'Customer Four');
insert into customer values (5, 'Customer Five');
insert into customer values (6, 'Customer Six');
insert into customer values (7, 'Customer Seven');
CREATE OR REPLACE PACKAGE cursor_not_array IS
FUNCTION get_customers(p_max_records INTEGER, p_id_start INTEGER, p_id_end INTEGER DEFAULT NULL) RETURN SYS_REFCURSOR;
END cursor_not_array;
CREATE OR REPLACE PACKAGE BODY cursor_not_array IS
c_max_customer_id CONSTANT NUMBER(2) := 99;
FUNCTION get_customers(p_max_records INTEGER, p_id_start INTEGER, p_id_end INTEGER DEFAULT NULL) RETURN SYS_REFCURSOR IS
v_result SYS_REFCURSOR;
BEGIN
OPEN v_result FOR
SELECT customer_id,
customer_name
FROM customer
WHERE customer_id BETWEEN p_id_start AND nvl(p_id_end, c_max_customer_id)
ORDER BY customer_id;
RETURN v_result;
END;
END cursor_not_array;
You could create a package like this:
create or replace package customers is
type customers_array is table of customer%rowtype index by binary_integer;
procedure get_customers(maxRows IN NUMBER, customer_array OUT customers_array);
end customers;
create or replace package body customers is
procedure get_customers(maxRows IN NUMBER, customer_array OUT customers_array) is
cursor c_customers is
select *
from customers;
where rownum <= maxRows;
i number := 1;
begin
for r in c_customers loop
customer_array(i) := r;
i := i + 1;
end loop;
end get_customers;
end customers;
And then call the get_customers procedure from wherever you want to...
first time create VARRAY type.
'create TYPE CUSTARRAY is VARRAY(100) OF VARCHAR2(30);'
varray limit is depends on you.
then create procedure that return CUSTARRAY type parameter.
`create
procedure prc_get_arr(p_maxrow in number, p_customers out custarray)
as
my_cust custarray := custarray();
cursor c_cust is select name from CUSTOMER where rownum<p_maxrow;
v_customer varchar2(64);
begin
open c_cust;
loop
fetch c_cust into v_customer;
exit when c_cust%notfound;
my_cust.extend;
my_cust(my_cust.count) := v_customer;
end loop;
close c_cust;
p_customers:=my_cust;
end;`
Now call this procedure
DECLARE
P_MAXROW NUMBER;
p_customers custarray;
v_cnt number:=0;
begin
P_MAXROW := 22;
prc_get_arr( p_maxrow => p_maxrow, p_customers => p_customers );
v_cnt:=p_customers.count;
for i in p_customers.first..p_customers.last loop
dbms_output.put_line('P_CUSTOMERS = ' || p_customers(i));
end loop;
end;
You can, using the SYS_REFCURSOR on your function output.
First you have to define a collection :
TYPE customers_array IS TABLE OF customer%ROWTYPE
INDEX BY BINARY_INTEGER;
Then your procedure simply have to fetch the result into that collection.
You're procedure could be written as follow:
CREATE OR REPLACE PACKAGE your_pkg
AS
TYPE customers_array IS TABLE OF customer%ROWTYPE
INDEX BY BINARY_INTEGER;
PROCEDURE get_customers(pn_max_rows IN NUMBER,
pt_coustomers OUT customers_array);
END your_pkg;
CREATE OR REPLACE PACKAGE BODY your_pkg
AS
PROCEDURE get_customers(pn_max_rows IN NUMBER,
pt_coustomers OUT customers_array)
IS
BEGIN
SELECT *
BULK COLLECT INTO pt_coustomers
FROM customers
WHERE rownum <= pn_max_rows;
END get_customers;
END your_pkg;
I have to write an Oracle procedure which should invoke an Oracle function returning REF_CURSOR. The function is declared like that
FUNCTION "IMPACTNET"."TF_CONVERTPARA" (PARASTRING IN NVARCHAR2) RETURN SYS_REFCURSOR
AS
c SYS_REFCURSOR;
BEGIN
OPEN c FOR
SELECT SUBSTR(element, 1, INSTR(element, '|') - 1) as key,
SUBSTR(element, INSTR(element, '|') + 1, 99999) as val
FROM (
SELECT REGEXP_SUBSTR(PARASTRING, '[^;]+', 1, LEVEL) element
FROM dual
CONNECT BY LEVEL < LENGTH(REGEXP_REPLACE(PARASTRING, '[^;]+')) + 1
);
RETURN c;
END;
Can you tell me what I need to write in order to invoke the function from within my procedure? I'd like to insert all the returned values (shaped a table with two columns) into a rational table.
Thank you in advance!
Something along the lines of this should work (obviously, I'm guessing about table names and column names and the exact logic that you're trying to implement)
CREATE PROCEDURE some_procedure_name
AS
l_rc SYS_REFCURSOR := impactnet.tf_convertpara( <<some string>> );
l_key VARCHAR2(100);
l_val VARCHAR2(100);
BEGIN
LOOP
FETCH l_rc
INTO l_key, l_val;
EXIT WHEN l_rc%notfound;
INSERT INTO some_table( key_column, val_column )
VALUES( l_key, l_val );
END LOOP;
END;
As Ollie points out, it would be more efficient to do a BULK COLLECT and a FORALL. If you're just dealing with a few thousand rows (since your function is just parsing the data in a delimited string, I'm assuming you expect relatively few rows to be returned), the performance difference is probably minimal. But if you're processing more data, the difference can be quite noticeable. Depending on the Oracle version and your specific requirements, you may be able to simplify the INSERT statement in the FORALL to insert a record rather than listing each column from the record individually.
CREATE PROCEDURE some_procedure_name
AS
TYPE key_val_rec
IS RECORD(
key VARCHAR2(100),
val VARCHAR2(100)
);
TYPE key_val_coll
IS TABLE OF key_val_rec;
l_rc SYS_REFCURSOR := impactnet.tf_convertpara( <<some string>> );
l_coll key_val_coll;
BEGIN
LOOP
FETCH l_rc
BULK COLLECT INTO l_coll
LIMIT 100;
EXIT WHEN l_coll.count = 0;
FORALL i IN l_coll.FIRST .. l_coll.LAST
INSERT INTO some_table( key_column, val_column )
VALUES( l_coll(i).key, l_coll(i).val );
END LOOP;
END;