How to Specify Array variable in plsql - oracle

Can any one please help me, how to assign result of select query to a array variable since result can be an array of values.

There are a couple of different approaches you could take to get data into your array. The first would be a simple loop, as in the following:
DECLARE
TYPE NUMBER_ARRAY IS VARRAY(100) OF NUMBER;
arrNums NUMBER_ARRAY;
i NUMBER := 1;
BEGIN
arrNums := NUMBER_ARRAY();
FOR aRow IN (SELECT NUMBER_FIELD
FROM A_TABLE
WHERE ROWNUM <= 100)
LOOP
arrNums.EXTEND;
arrNums(i) := aRow.SEQUENCE_NO;
i := i + 1;
END LOOP;
end;
Another, as suggested by #Rene, would be to use BULK COLLECT, as follows:
DECLARE
TYPE NUMBER_ARRAY IS VARRAY(100) OF NUMBER;
arrNums NUMBER_ARRAY;
BEGIN
arrNums := NUMBER_ARRAY();
arrNums.EXTEND(100);
SELECT NUMBER_FIELD
BULK COLLECT INTO arrNums
FROM A_TABLE
WHERE ROWNUM <= 100;
end;
Share and enjoy.

Related

PLSQL looping through JSON object

Basically I have a json that looks like this [{"group":"groupa","status":"active"},{"group":"groupb","status":"inactive"}] and I want to loop through and extract the group only and save them in a variable in order to loop and compare the groups to a certain variable.
for example
group := 'groupc'
while counter < jsonGroup.count
loop
if jsonGroup(counter) := group then ....
is there any way to save the group into jsonGroup as an array or table?
thank you
From Oracle 12, you can use JSON PL/SQL object types to iterate over the JSON array and to extract the value of the group attribute of the objects:
DECLARE
value VARCHAR2(4000) := '[{"group":"groupa","status":"active"},{"group":"groupb","status":"inactive"}]';
ja JSON_ARRAY_T := JSON_ARRAY_T.PARSE(value);
je JSON_ELEMENT_T;
grp VARCHAR2(20);
i PLS_INTEGER := 0;
BEGIN
LOOP
je := ja.GET(i);
EXIT WHEN je IS NULL;
grp := TREAT(je AS JSON_OBJECT_T).get_string('group');
DBMS_OUTPUT.PUT_LINE(grp);
i := i + 1;
END LOOP;
END;
/
Which outputs:
groupa
groupb
The values can be stored into an array using the JSON_ARRAY_T functionality as MT0 described or using JSON_TABLE which might be better if your JSON is already stored in a table.
Below is an example of how to use both methods to store the groups into an array.
DECLARE
l_json_text VARCHAR2 (100)
:= '[{"group":"groupa","status":"active"},{"group":"groupb","status":"inactive"}]';
TYPE tab_t IS TABLE OF VARCHAR2 (100);
l_table tab_t := tab_t ();
l_array json_array_t;
PROCEDURE print_tab
IS
BEGIN
FOR i IN 1 .. l_table.COUNT
LOOP
DBMS_OUTPUT.put_line (l_table (i));
END LOOP;
END;
BEGIN
l_array := json_array_t (l_json_text);
l_table.EXTEND (l_array.get_size);
FOR i IN 1 .. l_array.get_size
LOOP
l_table (i) := TREAT (l_array.get (i - 1) AS json_object_t).get_string ('group');
END LOOP;
DBMS_OUTPUT.put_line ('After JSON_ARRAY_T method');
print_tab;
l_table.delete;
DBMS_OUTPUT.put_line ('After delete');
print_tab;
SELECT grp
BULK COLLECT INTO l_table
FROM JSON_TABLE (l_json_text, '$[*]' COLUMNS grp PATH '$.group');
DBMS_OUTPUT.put_line ('After JSON_TABLE method');
print_tab;
END;
/

How to store a column of result of select query in an array?

If we have a column in a table of type number, how can we store the result of select query on that column in an array ?
This sample uses a list (table of numbers) to achieve this, because i find
those lists much more handy:
CREATE OR REPLACE TYPE numberlist AS TABLE OF NUMBER;
DECLARE
v_numberlist numberlist;
BEGIN
SELECT intval numbercolumn
BULK COLLECT INTO v_numberlist
FROM lookup;
FOR i IN 1..v_numberlist.count
LOOP
dbms_output.put_line( v_numberlist(i) );
END LOOP;
END;
Create a type which store number:-
CREATE OR REPLACE TYPE varray is table of number;
--write your select query inside for loop () where i am extracting through level
declare
p varray := varray();
BEGIN
for i in (select level from dual connect by level <= 10) loop
p.extend;
p(p.count) := i.level;
end loop;
for xarr in (select column_value from table(cast(p as varray))) loop
dbms_output.put_line(xarr.column_value);
end loop;
END;
output:-
1
2
3
4
5
6
7
8
9
10
Just an option to use some native SQL datatype. Hope it helps.
SET SERVEROUTPUT ON;
DECLARE
lv_num_tab DBMS_SQL.NUMBER_TABLE;
BEGIN
SELECT LEVEL BULK COLLECT INTO lv_num_tab FROM DUAL CONNECT BY LEVEL < 10;
FOR I IN lv_num_tab.FIRST..lv_num_tab.LAST
LOOP
dbms_output.put_line(lv_num_tab(i));
END LOOP;
END;
You may also want to put the whole select in a table. You can use a BULK COLLECT to an array:
CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
PROCEDURE get_tables(p_owner in varchar2)
as
v_res t_my_list;
v_qry varchar2(4000) := '';
begin
v_qry := ' SELECT table_name from all_tables where owner='''||p_owner||'''';
dbms_output.put_line(v_qry);
-- all at once in the table
execute immediate v_qry bulk collect into v_res;
FOR I in 1..v_res.count
loop
dbms_output.put_line(v_res(i));
end loop;
exception
when others then
raise;
end get_tables;
/
begin
get_tables('E') ;
end;
/

Use Oracle PL/SQL For Loop to iterate through comma delimited string

I am writing a piece of code that would need to iterate on the content of a string, each values being separated with a ,.
e.g. I have my elements
v_list_pak_like varchar2(4000) := 'PEBO,PTGC,PTTL,PTOP,PTA';
How can I get it into an Array / Cursor to iterate on it in my loop?
for x in (elements)
loop
-- do my stuff
end loop;
I am looking for the very simple way, if possible avoiding to declare associative arrays.
Would it be possible to create a function that would return something usable as an input for a for loop (opposite to the while that could be used like in https://stackoverflow.com/a/19184203/6019417)?
Many thanks in advance.
You could do it easily in pure SQL. there are multiple ways of doing it, see Split comma delimited string into rows in Oracle
However, if you really want to do it in PL/SQL, then you could do it as:
SQL> set serveroutput on
SQL> DECLARE
2 str VARCHAR2(100) := 'PEBO,PTGC,PTTL,PTOP,PTA';
3 BEGIN
4 FOR i IN
5 (SELECT trim(regexp_substr(str, '[^,]+', 1, LEVEL)) l
6 FROM dual
7 CONNECT BY LEVEL <= regexp_count(str, ',')+1
8 )
9 LOOP
10 dbms_output.put_line(i.l);
11 END LOOP;
12 END;
13 /
PEBO
PTGC
PTTL
PTOP
PTA
PL/SQL procedure successfully completed.
SQL>
Thanks to Lalit great instructions, I am able to create a function that I can call from my for loop:
Create a type and function
CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
FUNCTION comma_to_table(p_list IN VARCHAR2)
RETURN t_my_list
AS
l_string VARCHAR2(32767) := p_list || ',';
l_comma_index PLS_INTEGER;
l_index PLS_INTEGER := 1;
l_tab t_my_list := t_my_list();
BEGIN
LOOP
l_comma_index := INSTR(l_string, ',', l_index);
EXIT
WHEN l_comma_index = 0;
l_tab.EXTEND;
l_tab(l_tab.COUNT) := TRIM(SUBSTR(l_string,l_index,l_comma_index - l_index));
l_index := l_comma_index + 1;
END LOOP;
RETURN l_tab;
END comma_to_table;
/
Then how to call it in my for loop:
declare
v_list_pak_like varchar2(4000) := 'PEBO,PTGC,PTTL,PTOP,PTA';
begin
FOR x IN (select * from (table(comma_to_table(v_list_pak_like)) ) )
loop
dbms_output.put_line(x.COLUMN_VALUE);
end loop;
end;
/
Notice the default name COLUMN_VALUE given by Oracle that is necessary for the use I want to make of the result.
Result as expected:
PEBO
PTGC
PTTL
PTOP
PTA
declare
type array_type is table of VARCHAR2(255) NOT NULL;
my_array array_type := array_type('aaa','bbb','ccc');
begin
for i in my_array.first..my_array.last loop
dbms_output.put_line( my_array(i) );
end loop;
end;
In the first line you define a table of any type you want.
then create variable of that type and give it values with a kind of constructor.
I initialized it in the declaration but it can be done also in the body of the Pl Sql.
Then just loop over your array from first index to the last.

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;

Convert array of records to refcursor

The question is how to return the l_array as refcursor,
Since the interface i am using can handle cursor easily rather than an array of record.
Plz help
create or replace package sample
TYPE r_type is record( code number; description varchar2(50));
TYPE tr_type IS TABLE OF r_type; l_rarray tr_type ; ind number:=0;
PROCEDURE getdata() IS
CURSOR cur IS
SELECT empid, empname, place, location FROM emp;
TYPE epmid_aat IS TABLE OF emp.empid%TYPE INDEX BY BINARY_INTEGER;
l_empid empid_aat;
BEGIN
k := 1;
FOR j IN (SELECT DISTINCT empid FROM emp)
LOOP
l_empid(k) := j.empid;
k := k + 1;
END LOOP;
FOR i IN cur
LOOP
FOR k IN l_empid.first .. l_empid.last
LOOP
IF l_empid(k) = i.empid THEN
procedure2(i.emp_id);
END IF;
END LOOP;
END LOOP;
END getdata();
PROCEDURE procedure2
(
empid_in IN NUMBER,
description_in IN VARCHAR2(20)
) IS
BEGIN
lrec.code := empid_in;
lrec.description := description_in;
l_rarray(ind) := lrec;
ind := ind + 1;
END procedure2;
end;
I think it should be like this :
OPEN YourRefCursor
FOR SELECT * FROM TABLE (Cast(l_rarray AS tr_type));
Something like this.
TYPE r_type is record ( code number;
description varchar2(50)
);
TYPE tr_type IS TABLE OF r_type;
l_rarray tr_type ;
SELECT r_type(empid, empname)
BULK COLLECT INTO l_rarray
FROM emp;
OPEN YourRefCursor
SELECT *
FROM TABLE (Cast(l_rarray AS r_type));

Resources