merging two indexed nested tables per unique value in oracle plsql - oracle

I have this record type, a list of VARCHAR2 :
TYPE varchar2_array IS TABLE OF VARCHAR2(255) INDEX BY BINARY_INTEGER;
In my PLSQL, I have two of these arrays, and I would like to merge them into a third array, per unique value. Something like :
DECLARE
TYPE varchar2_array IS TABLE OF VARCHAR2(255) INDEX BY BINARY_INTEGER;
foo varchar2_array;
bar varchar2_array;
foobar varchar2_array;
BEGIN
foo(1) := '1';
foo(2) := '2';
foo(3) := '3';
bar(1) := '2';
bar(2) := '3';
bar(3) := '4';
foobar := unique_merge (foo, bar);
FOR i IN 1 .. foobar.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('foobar(' || i || ') : ' || foobar(i));
END LOOP;
-- output : 1, 2, 3, 4
END;
I am looking for a simple way to do this. Thanks.

Try MULTISET UNION DISTINCT.
The solution could be something like
customer_list_3 := customer_list_2 MULTISET UNION DISTINCT customer_list_1
EDIT:
Ok, I did a mistrake, I didn't see this is not a nested table.
Here a possible impelmentation:
DECLARE
TYPE VAR_TABLE_TYPE IS TABLE OF VARCHAR2(10);
TYPE varchar2_array IS TABLE OF VARCHAR2(255) INDEX BY BINARY_INTEGER;
foo varchar2_array;
bar varchar2_array;
foobar varchar2_array;
foobar_tmp VAR_TABLE_TYPE:= VAR_TABLE_TYPE() ;
j number;
BEGIN
foo(1) := '1';
foo(2) := '2';
foo(3) := '3';
bar(1) := '2';
bar(2) := '3';
bar(3) := '4';
j:=foo.count;
for i in foo.first .. foo.last
loop
foobar_tmp.EXtend;
foobar_tmp(i) := foo(i) ;
end loop;
for i in bar.first .. bar.last
loop
foobar_tmp.EXtend;
foobar_tmp(j+i) := bar(i) ;
end loop;
foobar_tmp := SET (foobar_tmp) ;
for i in foobar_tmp.first .. foobar_tmp.last
loop
foobar(i) := foobar_tmp(i) ;
end loop;
for i in foobar.first .. foobar.last
loop
DBMS_OUTPUT.PUT_LINE('foobar(' || i || ') '||foobar(i));
end loop;
END;

Related

Looping through a nested table type PLSQL

I have a record type:
TYPE SOME_RECTYPE IS RECORD
(attr1 ATTR1_TBLTYPE,
attr2 ATTR2_TBLTYPE,
rec_dtl RECDTL_TBLTYPE
);
where ATTR1_TBLTYPE and ATTR2_TBLTYPE are tables of a DB column, something like:
TYPE ATTR1_TBLTYPE IS TABLE OF SOME_TABLE.ATTR1%TYPE INDEX BY BINARY_INTEGER;
and RECDTL_TBLTYPE is a table of another record type:
TYPE SOMEOTHER_RECTYPE IS RECORD
(attr3 ATTR3_TBLTYPE,
attr4 ATTR4_TBLTYPE
);
How do I retrieve the values of attr3 and attr4?
I tried looping like this:
FOR i IN attr1.FIRST..attr1.LAST LOOP
dbms_output.put_line(attr1(i));
dbms_output.put_line(attr2(i));
FOR j in rec_dtl(i).attr3.FIRST..rec_dtl(i).attr3.LAST LOOP
dbms_output.put_line(attr3(i));
END LOOP;
END LOOP;
It is giving me ORA-01403: no data found error
It is because of the indexing : rec_dtl(i).attr3
So, how to iterate the loop to fetch the values of attr3 and attr4?
You are using associative arrays and there is no guarantee that they will not be sparse so you should use assoc_array.FIRST to get the first index and then assoc_array.NEXT( index ) in a loop to iterate through indexes.
DECLARE
TYPE ATTR1_TBLTYPE IS TABLE OF SOME_TABLE.ATTR1%TYPE INDEX BY BINARY_INTEGER;
TYPE ATTR2_TBLTYPE IS TABLE OF SOME_TABLE.ATTR2%TYPE INDEX BY BINARY_INTEGER;
TYPE ATTR3_TBLTYPE IS TABLE OF SOME_TABLE.ATTR3%TYPE INDEX BY BINARY_INTEGER;
TYPE ATTR4_TBLTYPE IS TABLE OF SOME_TABLE.ATTR4%TYPE INDEX BY BINARY_INTEGER;
TYPE RECDTL_TBLTYPE IS RECORD(
attr3 ATTR3_TBLTYPE,
attr4 ATTR4_TBLTYPE
);
TYPE SOME_RECTYPE IS RECORD(
attr1 ATTR1_TBLTYPE,
attr2 ATTR2_TBLTYPE,
rec_dtl RECDTL_TBLTYPE
);
value SOME_RECTYPE;
i BINARY_INTEGER;
BEGIN
-- Sample Data
value.attr1(1) := 'Hello';
value.attr1(3) := 'World';
value.attr2(-9) := 0;
value.rec_dtl.attr3(1) := TRUNC(SYSDATE);
value.rec_dtl.attr3(2) := DATE '1970-01-01';
value.rec_dtl.attr4(0) := EMPTY_CLOB();
-- Loop Through attr1:
i := value.attr1.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE( 'value.attr1(' || i || ') = ' || value.attr1(i) );
i := value.attr1.NEXT(i);
END LOOP;
-- Loop through rec_dtl.attr3:
i := value.rec_dtl.attr3.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE( 'value.rec_dtl.attr3(' || i || ') = ' || value.rec_dtl.attr3(i) );
i := value.rec_dtl.attr3.NEXT(i);
END LOOP;
END;
/
Outputs:
value.attr1(1) = Hello
value.attr1(3) = World
value.rec_dtl.attr3(1) = 22-AUG-19
value.rec_dtl.attr3(2) = 01-JAN-70
db<>fiddle here
TYPE ATTR1_TBLTYPE IS TABLE OF xxxx INDEX BY BINARY_INTEGER
is not the same as
TYPE ATTR2_TBLTYPE IS TABLE OF xxxx
For the 2-nd you are sure that your collection is dens and you can iterate it using for..loop
For 1st you should use approach with key word next.
declare
type type1 is table of varchar2(4000) index by binary_integer;
v1 type1;
idx binary_integer;
begin
v1(1) := 'a';
v1(2) := 'b';
v1(5) := 'c';
v1(-10) := 'Collection is allways sorted by index';
idx := v1.first;
while (idx is not null) loop
dbms_output.put_line(v1(idx)||'- is value of index '||idx);
idx := v1.next(idx);
end loop;
end;

How can I use MOD function within WHILE LOOP with PLS_INTEGER data type?

I'm trying to print on screen odd values of an associative array using a simple "WHILE LOOP" with MOD condition. Is it possible? I know that PLS_INTEGER only accept not decimal values (like int datatype on Java). So... I tried with a NUMBER counter but I receive the same results. How Can I resolve it? . Thanks
SET SERVEROUTPUT ON
DECLARE
TYPE type_test IS TABLE OF VARCHAR2(45)
INDEX BY PLS_INTEGER;
t_test_5 type_test;
v_counter_1 PLS_INTEGER;
v_counter_2 NUMBER;
BEGIN
t_test_5(1) := 'Test1';
t_test_5(2) := 'Test2';
t_test_5(3) := 'Test3';
t_test_5(4) := 'Test4';
t_test_5(5) := 'Test5';
t_test_5(6) := 'Test6';
t_test_5(7) := 'Test7';
t_test_5(8) := 'Test8';
t_test_5(9) := 'Test9';
t_test_5(10) := 'Test10';
DBMS_OUTPUT.PUT_LINE('PLS_INTEGER COUNTER TEST');
v_counter_1 := t_test_5.FIRST;
WHILE MOD(v_counter_1, 2) <> 0
LOOP
DBMS_OUTPUT.PUT_LINE(t_test_5(v_counter_1));
v_counter_1 := t_test_5.NEXT(v_counter_1);
END LOOP;
DBMS_OUTPUT.PUT_LINE(' ');
DBMS_OUTPUT.PUT_LINE('NUMBER COUNTER TEST');
v_counter_2 := t_test_5.FIRST;
WHILE MOD(v_counter_2, 2) <> 0
LOOP
DBMS_OUTPUT.PUT_LINE(t_test_5(v_counter_2));
v_counter_2 := t_test_5.NEXT(v_counter_2);
END LOOP;
END;
I want to retrieve on screen the values 1, 3, 5, 7, 9 but in both situations I only retrieve value 1:
Procedimiento PL/SQL terminado correctamente.
PLS_INTEGER COUNTER TEST
Test1
NUMBER COUNTER TEST
Test1
The issue is not in the type of your variables, but in the fact that your loops end at the first row that does not match MOD(v_counter_1, 2) <> 0, thus not scanning all the rows.
What you need is not a loop ending when MOD(v_counter_1, 2) = 0, but a loop that scans all the rows, simply printing the values for the only rows that match your criteria:
DECLARE
TYPE type_test IS TABLE OF VARCHAR2(45)
INDEX BY PLS_INTEGER;
t_test_5 type_test;
v_counter_1 PLS_INTEGER;
v_counter_2 NUMBER;
BEGIN
t_test_5(1) := 'Test1';
t_test_5(2) := 'Test2';
t_test_5(3) := 'Test3';
t_test_5(4) := 'Test4';
t_test_5(5) := 'Test5';
t_test_5(6) := 'Test6';
t_test_5(7) := 'Test7';
t_test_5(8) := 'Test8';
t_test_5(9) := 'Test9';
t_test_5(10) := 'Test10';
DBMS_OUTPUT.PUT_LINE('PLS_INTEGER COUNTER TEST');
v_counter_1 := t_test_5.FIRST;
WHILE v_counter_1 is not null
LOOP
if mod(v_counter_1, 2) != 0 then
DBMS_OUTPUT.PUT_LINE(t_test_5(v_counter_1));
end if;
v_counter_1 := t_test_5.NEXT(v_counter_1);
END LOOP;
END;
the result:
PLS_INTEGER COUNTER TEST
Test1
Test3
Test5
Test7
Test9
Your while loop ends as soon as the counter's value is an even number - so as soon as it reaches 2 then loop will end. What you want is to loop through all the values but skip the even values:
WHILE ( v_counter_1 IS NOT NULL )
LOOP
IF MOD( v_counter_1, 2 ) = 0 THEN
v_counter_1 := t_test_5.NEXT(v_counter_1);
CONTINUE;
END IF;
DBMS_OUTPUT.PUT_LINE(t_test_5(v_counter_1));
v_counter_1 := t_test_5.NEXT(v_counter_1);
END LOOP;
If you are not going to have a sparse array then you do not need to use an associative array:
DECLARE
TYPE type_test IS TABLE OF VARCHAR2(45);
t type_test := type_test( 'Test1', 'Test2', 'Test3', 'Test4', 'Test5', 'Test6' );
BEGIN
FOR i = 1 .. t.COUNT / 2 LOOP
DBMS_OUTPUT.PUT_LINE(t(2*i-1));
END LOOP;
END;
/

Can I iterate over columns of a composite type?

Let's say I have the following table named bar:
key | columnA | columnB | columnC
A | B | C | D
E | F | G | H
I want to write a function taking a key and a string and doing the following (best described by examples):
Input: ('A', '${columnB} - ${columnA}') / Output : 'C - B'
Input: ('B', 'Hello ${columnC}') / Output: 'Hello H'
For the moment, I have this implementation:
CREATE OR REPLACE FUNCTION foo
( param_key IN VARCHAR2
, format_string IN VARCHAR2
)
RETURN VARCHAR2
IS
my_row bar%ROWTYPE;
retval VARCHAR2(4000);
BEGIN
BEGIN SELECT * INTO my_row FROM bar WHERE "key" = param_key;
EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL;
END;
retval := format_string;
retval := REPLACE(retval, '${columnA}', my_row.columnA);
retval := REPLACE(retval, '${columnB}', my_row.columnB);
retval := REPLACE(retval, '${columnC}', my_row.columnC);
RETURN retval;
END;
/
I would like to avoid enumerating all columns one by one in the last part, because the structure of my table can change (new columns for instance). Is there a way to iterate on all columns of my_row, and to replace ${the column name} with the value stored in that column, in a generic way?
Thank you
Another way to achieve this.
Create xmltype from table row.
Create xsl-transform from format_string.
Transform xml using xsl
declare
v_string_format varchar2(200) := '{columnA} + {columnB} + {columnA}{columnB}';
v_key varchar2(10) := 'A';
v_cursor sys_refcursor;
l_xml xmltype;
v_xslt VARCHAR2(500):='<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="/ROWSET/ROW">{patern}</xsl:template></xsl:stylesheet>';
begin
-- create xsl transform
v_string_format := upper(v_string_format);
v_string_format := REPLACE(v_string_format,'{','<xsl:value-of select="');
v_string_format := REPLACE(v_string_format,'}','"/>');
v_xslt := replace(v_xslt,'{patern}',v_string_format);
dbms_output.put_line(v_string_format);
-- open cursor for table
open v_cursor for select * from bar where key = v_key;
-- get v_cursor as xmltype.
l_xml := xmltype(v_cursor);
-- print xml
dbms_output.put_line(l_xml.getClobVal());
-- tranform xml and print result
dbms_output.put_line(l_xml.transform(xmltype(v_xslt)).getClobVal());
close v_cursor;
end;
A more efficient solution is this one. For sure you have to write more code and it uses the full scope of dynamic SQL.
CREATE OR REPLACE FUNCTION foo (param_key IN VARCHAR2, format_string IN VARCHAR2)
RETURN VARCHAR2 IS
retval VARCHAR2(4000) := format_string;
cur SYS_REFCURSOR;
curId INTEGER;
descTab DBMS_SQL.DESC_TAB;
colCnt NUMBER;
numvar NUMBER;
datevar DATE;
namevar VARCHAR2(4000);
tsvar TIMESTAMP;
BEGIN
OPEN cur FOR SELECT * FROM bar WHERE "key" = param_key;
curId := DBMS_SQL.TO_CURSOR_NUMBER(cur);
DBMS_SQL.DESCRIBE_COLUMNS(curId, colCnt, descTab);
-- Define columns
FOR i IN 1..colcnt LOOP
IF desctab(i).col_type = DBMS_TYPES.TYPECODE_NUMBER THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, numvar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_DATE THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, datevar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_TIMESTAMP THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, tsvar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_VARCHAR2 THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, namevar, 4000);
--ELSIF desctab(i).col_type = ... THEN
--DBMS_SQL.DEFINE_COLUMN(curid, i, ...);
END IF;
END LOOP;
-- Fetch Rows
IF DBMS_SQL.FETCH_ROWS(curid) > 0 THEN
-- Fetch only the first row and do not consider if further rows exist,
-- otherwise use WHILE DBMS_SQL.FETCH_ROWS(curid) > 0 LOOP
FOR i IN 1..colcnt LOOP
IF desctab(i).col_type = DBMS_TYPES.TYPECODE_VARCHAR2 THEN
DBMS_SQL.COLUMN_VALUE(curid, i, namevar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', namevar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_NUMBER THEN
DBMS_SQL.COLUMN_VALUE(curid, i, numvar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', numvar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_DATE THEN
DBMS_SQL.COLUMN_VALUE(curid, i, datevar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', datevar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_TIMESTAMP THEN
DBMS_SQL.COLUMN_VALUE(curid, i, tsvar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', tsvar);
--ELSIF desctab(i).col_type = ... THEN
--DBMS_SQL.COLUMN_VALUE(curid, i, ...);
--retval := REPLACE(retval, '${'||desctab(i).col_name||'}', ...);
END IF;
END LOOP;
ELSE
retval := NULL;
END IF;
DBMS_SQL.CLOSE_CURSOR(curId);
RETURN retval;
END;
You can get the result you are after using dynamic queries...
CREATE OR REPLACE FUNCTION foo
( param_key IN VARCHAR2
, format_string IN VARCHAR2
)
RETURN VARCHAR2
IS
retval VARCHAR2(4000) := format_string;
cols SYS.ODCIVARCHAR2LIST;
BEGIN
SELECT COLUMN_NAME
BULK COLLECT INTO cols
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'bar'
ORDER BY COLUMN_ID;
FOR i IN 1 .. cols.COUNT LOOP
EXECUTE IMMEDIATE 'SELECT REPLACE( :1, ''${' || cols(i) || '}'', ' || cols(i) || ' ) FROM bar WHERE key = :2'
INTO retval
USING retval, param_key;
END LOOP;
RETURN retval;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END;
/
... but:
This uses dynamic SQL to query the table directly and does not use a %ROWTYPE record.
You may not have access to USER_TAB_COLUMNS (or may need ALL_TAB_COLUMNS) and the DBA might not want you to have access to the data dictionary tables.
It is probably (almost certainly) very inefficient.
I've seen this done before and never let it pass a code review (writing out the explicit column names has always seemed preferable).
So, while it is possible, I would say don't do this.

get key and value of associative arrays

My problem is that i want to get key and value from an assosiative array, but i only can find how to get the value from the key. This is what i find:
DECLARE
TYPE assoc_array IS TABLE OF VARCHAR2(30)
INDEX BY VARCHAR2(30);
state_array assoc_array;
BEGIN
state_array('Alaska') := 'Juneau';
state_array('California') := 'Sacramento';
dbms_output.put_line(state_array('Alaska'));
dbms_output.put_line(state_array('California'));
END;
This prints Juneau and Sacramento, but i want something like this:
DECLARE
TYPE assoc_array IS TABLE OF VARCHAR2(30)
INDEX BY VARCHAR2(30);
state_array assoc_array;
BEGIN
state_array('Alaska') := 'Juneau';
state_array('California') := 'Sacramento';
for x in 1..state_array.count loop
dbms_output.put_line(state_array(x).key || state_array(x).value);
end loop;
END;
Is that possible?. Thanks in advance!!
Actually there is a way, kindly consider the code bellow
declare
type assoc_array is table of varchar2(30) index by varchar2(30);
state_array assoc_array;
l_idx varchar2(30);
begin
state_array('Alaska') := 'Juneau';
state_array('California') := 'Sacramento';
l_idx := state_array.first;
while (l_idx is not null) loop
dbms_output.put_line('Key = ' || l_idx || ':Value = ' || state_array(l_idx));
l_idx := state_array.next(l_idx);
end loop;
end;
The output will be
Key = Alaska:Value = Juneau
Key = California:Value = Sacramento
You cannot do this using associative arrays. Because, see below.
DECLARE
TYPE assoc_array IS TABLE OF VARCHAR2(30)
INDEX BY VARCHAR2(30);
state_array assoc_array;
BEGIN
state_array('Alaska') := 'Juneau';
state_array('California') := 'Sacramento';
for x in 1..state_array.count loop
dbms_output.put_line('x ='||x);
end loop;
END;
would print
x = 1
x = 2
There is no way for oracle to know that x = 1 = Alaska.
You should use binary array to do something like this.

Oracle PL/SQL - How to create a simple array variable?

I'd like to create an in-memory array variable that can be used in my PL/SQL code. I can't find any collections in Oracle PL/SQL that uses pure memory, they all seem to be associated with tables. I'm looking to do something like this in my PL/SQL (C# syntax):
string[] arrayvalues = new string[3] {"Matt", "Joanne", "Robert"};
Edit:
Oracle: 9i
You can use VARRAY for a fixed-size array:
declare
type array_t is varray(3) of varchar2(10);
array array_t := array_t('Matt', 'Joanne', 'Robert');
begin
for i in 1..array.count loop
dbms_output.put_line(array(i));
end loop;
end;
Or TABLE for an unbounded array:
...
type array_t is table of varchar2(10);
...
The word "table" here has nothing to do with database tables, confusingly. Both methods create in-memory arrays.
With either of these you need to both initialise and extend the collection before adding elements:
declare
type array_t is varray(3) of varchar2(10);
array array_t := array_t(); -- Initialise it
begin
for i in 1..3 loop
array.extend(); -- Extend it
array(i) := 'x';
end loop;
end;
The first index is 1 not 0.
You could just declare a DBMS_SQL.VARCHAR2_TABLE to hold an in-memory variable length array indexed by a BINARY_INTEGER:
DECLARE
name_array dbms_sql.varchar2_table;
BEGIN
name_array(1) := 'Tim';
name_array(2) := 'Daisy';
name_array(3) := 'Mike';
name_array(4) := 'Marsha';
--
FOR i IN name_array.FIRST .. name_array.LAST
LOOP
-- Do something
END LOOP;
END;
You could use an associative array (used to be called PL/SQL tables) as they are an in-memory array.
DECLARE
TYPE employee_arraytype IS TABLE OF employee%ROWTYPE
INDEX BY PLS_INTEGER;
employee_array employee_arraytype;
BEGIN
SELECT *
BULK COLLECT INTO employee_array
FROM employee
WHERE department = 10;
--
FOR i IN employee_array.FIRST .. employee_array.LAST
LOOP
-- Do something
END LOOP;
END;
The associative array can hold any make up of record types.
Hope it helps,
Ollie.
You can also use an oracle defined collection
DECLARE
arrayvalues sys.odcivarchar2list;
BEGIN
arrayvalues := sys.odcivarchar2list('Matt','Joanne','Robert');
FOR x IN ( SELECT m.column_value m_value
FROM table(arrayvalues) m )
LOOP
dbms_output.put_line (x.m_value||' is a good pal');
END LOOP;
END;
I would use in-memory array. But with the .COUNT improvement suggested by uziberia:
DECLARE
TYPE t_people IS TABLE OF varchar2(10) INDEX BY PLS_INTEGER;
arrayvalues t_people;
BEGIN
SELECT *
BULK COLLECT INTO arrayvalues
FROM (select 'Matt' m_value from dual union all
select 'Joanne' from dual union all
select 'Robert' from dual
)
;
--
FOR i IN 1 .. arrayvalues.COUNT
LOOP
dbms_output.put_line(arrayvalues(i)||' is my friend');
END LOOP;
END;
Another solution would be to use a Hashmap like #Jchomel did here.
NB:
With Oracle 12c you can even query arrays directly now!
Another solution is to use an Oracle Collection as a Hashmap:
declare
-- create a type for your "Array" - it can be of any kind, record might be useful
type hash_map is table of varchar2(1000) index by varchar2(30);
my_hmap hash_map ;
-- i will be your iterator: it must be of the index's type
i varchar2(30);
begin
my_hmap('a') := 'apple';
my_hmap('b') := 'box';
my_hmap('c') := 'crow';
-- then how you use it:
dbms_output.put_line (my_hmap('c')) ;
-- or to loop on every element - it's a "collection"
i := my_hmap.FIRST;
while (i is not null) loop
dbms_output.put_line(my_hmap(i));
i := my_hmap.NEXT(i);
end loop;
end;
Sample programs as follows and provided on link also https://oracle-concepts-learning.blogspot.com/
plsql table or associated array.
DECLARE
TYPE salary IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
salary_list salary;
name VARCHAR2(20);
BEGIN
-- adding elements to the table
salary_list('Rajnish') := 62000; salary_list('Minakshi') := 75000;
salary_list('Martin') := 100000; salary_list('James') := 78000;
-- printing the table name := salary_list.FIRST; WHILE name IS NOT null
LOOP
dbms_output.put_line ('Salary of ' || name || ' is ' ||
TO_CHAR(salary_list(name)));
name := salary_list.NEXT(name);
END LOOP;
END;
/
Using varray is about the quickest way to duplicate the C# code that I have found without using a table.
Declare your public array type to be use in script
type t_array is varray(10) of varchar2(60);
This is the function you need to call - simply finds the values in the string passed in using a comma delimiter
function ConvertToArray(p_list IN VARCHAR2)
RETURN t_array
AS
myEmailArray t_array := t_array(); --init empty array
l_string varchar2(1000) := p_list || ','; - (list coming into function adding final comma)
l_comma_idx integer;
l_index integer := 1;
l_arr_idx integer := 1;
l_email varchar2(60);
BEGIN
LOOP
l_comma_idx := INSTR(l_string, ',', l_index);
EXIT WHEN l_comma_idx = 0;
l_email:= SUBSTR(l_string, l_index, l_comma_idx - l_index);
dbms_output.put_line(l_arr_idx || ' - ' || l_email);
myEmailArray.extend;
myEmailArray(l_arr_idx) := l_email;
l_index := l_comma_idx + 1;
l_arr_idx := l_arr_idx + 1;
END LOOP;
for i in 1..myEmailArray.count loop
dbms_output.put_line(myEmailArray(i));
end loop;
dbms_output.put_line('return count ' || myEmailArray.count);
RETURN myEmailArray;
--exception
--when others then
--do something
end ConvertToArray;
Finally Declare a local variable, call the function and loop through what is returned
l_array t_array;
l_Array := ConvertToArray('email1#gmail.com,email2#gmail.com,email3#gmail.com');
for idx in 1 .. l_array.count
loop
l_EmailTo := Trim(replace(l_arrayXX(idx),'"',''));
if nvl(l_EmailTo,'#') = '#' then
dbms_output.put_line('Empty: l_EmailTo:' || to_char(idx) || l_EmailTo);
else
dbms_output.put_line
( 'Email ' || to_char(idx) ||
' of array contains: ' ||
l_EmailTo
);
end if;
end loop;

Resources