PLSQL Collections - how to use table of records? - oracle

I'm new to PL/SQL and I'm trying to use a table of records, but I don't know how to use this feature. What is the problem?
DECLARE
TYPE TIP
IS
RECORD
(
F1 SMALLINT,
F2 SMALLINT);
TYPE Ve
IS
TABLE OF TIP;
v ve;
IND SMALLINT := 0;
BEGIN
WHILE(IND<20)
LOOP
IND := IND + 1;
V(IND).F2 := IND-1;
V(IND).F2 := IND;
END LOOP;
END;
What I'm doing wrong?
06531. 00000 - "Reference to uninitialized collection"

You need to make two changes.
v ve := ve();
This initializes the V array. This will create an empty VE table.
IND := IND + 1;
v.extend(IND+1);
V(IND).F2 := IND-1;
V(IND).F2 := IND;
Next is the call to v.EXTEND() to make sure the table has enough entries to hold your values. This now functions correctly.

Related

Inserting values into a created table with while loop

I'm working on a while loop exercise on oracle. I have created a table with two columns.
What I want to do is; inserting values into first column with a sequence of from 1 to 1 million(1,2,3,4,5....1000000).
I've tried
DECLARE
a int := 0;
BEGIN
WHILE a < 1000000 LOOP
a := a + 1;
END LOOP;
END;
insert into Schema_name.table_name
(column_1)
values('a')
P.S: I'm working on Toad 12.9
Would you like to give a hand to me for this?
Just insert values(a), when you write 'a' you insert the character 'a' and not the variable a
DECLARE
a int := 0;
BEGIN
WHILE a < 1000000 LOOP
a := a + 1;
insert into Schema_name.table_name
(column_1)
values(a);
END LOOP;
END;

inserting of data not happening in oracle

I am new to plsql.I have a table where i need to insert the data(some dummy data).So,i thought to use plsql block and using For loop it will insert the data automatically.The plsql block is runned successfully,but the data are stored as empty.The block I tried is:
declare
v_number1 number;
v_number2 number;
v_number3 number;
begin
For Lcntr IN 2..17
LOOP
v_number1 := v_number1+1;
v_number2 := v_number2+2;
v_number3 := v_number3+3;
Insert into stu.result(res_id,stu_id,eng,maths,science) values (stu.seq_no.NEXTVAL,Lcntr,v_number1,v_number2,v_number3);
END LOOP;
end;
But my table is loaded as:(please ignore first two row data,i inserted it manually):
The data for eng,maths,science is not being inserted.why it is happening so?
That's because your variables are NULL. NULL + 1 = NULL as well.
If you modify declaration to
v_number1 number := 0;
v_number2 number := 0;
v_number3 number := 0;
something might happen.

Creating function that returns table that isn't declared yet

Our product has several components that can be installed separately and supports oracle. But we do not grant Create type privilege. So during installation of one of the components, I need to ask customer to install the component, add Create type privilege and then run my component.
In the SQL file that will create functions I was planning to give following code:
CREATE OR REPLACE FUNCTION get_some_data (input INT)
RETURN my_table
AS
my_table_var my_table := my_table ();
ret_code INT := 0;
BEGIN
ret_code := create_my_type_and_table ();
IF 1 = ret_code
THEN
NULL; -- add some data to my_table_var here
END IF;
RETURN my_table_var;
END get_some_data;
/
The function create_my_type_and_table would use execute immediate to create record and table type.
Obviously, the problem is since function get_some_data says it will return my_table, compilation fails.
I wanted to know:
is there a way out?
the reason why I want to create and return table is because I need to return multiple fields. All of them are int. Is there a way I can return multi dimensional array, perhaps system collection? I tried sys.odcinumberlist but I did not find a way by which I can return 4 columned sys.odcinumberlist.
If you want some anonymous generic stuff, you could use TABLE OF [TYPE].
Here an example how to build a table of number which is used by another type "table of table of number".
Like every type, you can create them within your schema to use it between different plsql-blocks or return-value in functions.
declare
TYPE tableOfNumber is Table of Number; -- Define a row of numbers
TYPE tableOfTableOfNumer is Table of tableOfNumber; -- define a table of rows
tableMaster tableOfTableOfNumer := tableOfTableOfNumer(); -- init tables
tableChild1 tableOfNumber := tableOfNumber();
tableChild2 tableOfNumber := tableOfNumber();
begin
tableChild1.Extend; -- add a new number-field to our table
tableChild1(1) := 0; -- set the value to the new field
tableChild1.Extend;
tableChild1(2) := 1;
tableChild2.Extend;
tableChild2(1) := 2;
tableChild2.Extend;
tableChild2(2) := 3;
tableMaster.Extend; -- add a new 'row' to out table
tableMaster(1) := tableChild1; -- fill the new 'row' with the fields
tableMaster.Extend;
tableMaster(2) := tableChild2;
-- loop through our 'table'
for r in 1 .. tableMaster.Count
LOOP
for c in 1 .. tableMaster(r).Count
LOOP
dbms_output.put_line(tableMaster(r)(c));
END LOOP;
END LOOP;
end;
If you want to create a function you have to:
1. Declare types in you schema
(an grant rights to the users)
CREATE TYPE tableOfNumber AS TABLE OF NUMBER;
CREATE TYPE tableOftableOfNumber AS TABLE OF tableOfNumber;
2. Create you function:
(here with same code like the plsql-block)
CREATE OR REPLACE FUNCTION DINTER.MyFunction
RETURN tableOftableOfNumber
Is
tableMaster tableOftableOfNumber := tableOftableOfNumber(); -- init tables
tableChild1 tableOfNumber := tableOfNumber();
tableChild2 tableOfNumber := tableOfNumber();
begin
tableChild1.Extend; -- add a new number-field to our table
tableChild1(1) := 0; -- set the value to the new field
tableChild1.Extend;
tableChild1(2) := 1;
tableChild2.Extend;
tableChild2(1) := 2;
tableChild2.Extend;
tableChild2(2) := 3;
tableMaster.Extend; -- add a new 'row' to out table
tableMaster(1) := tableChild1; -- fill the new 'row' with the fields
tableMaster.Extend;
tableMaster(2) := tableChild2;
RETURN tableMaster;
end MyFunction;
/
3. Call the function:
declare
tableMaster tableOfTableOfNumber;
begin
tableMaster := myfunction();
-- loop through our 'table'
for r in 1 .. tableMaster.Count
LOOP
for c in 1 .. tableMaster(r).Count
LOOP
dbms_output.put_line(tableMaster(r)(c));
END LOOP;
END LOOP;
end;

Associative array issue

I have created an associative array, I understand it can be used different way of writing but however just need tips how to make this work. Currently when I compile this block I would receive no data found. Thank you!
DECLARE
TYPE type_state IS TABLE OF VARCHAR(50)
INDEX BY VARCHAR2(50);
tbl_state type_state;
lv_statecity1_txt VARCHAR2(30):= 'TAMPA';
lv_statecity2_txt VARCHAR2(30):= 'ATLANTA';
lv_statecity3_txt VARCHAR2(30):= 'NYC';
lv_cnt_num NUMBER(5) := 0;
BEGIN
tbl_state('FLORIDA') := lv_statecity1_txt;
tbl_state('GEORGIA') := lv_statecity2_txt;
tbl_state('New_York') := lv_statecity3_txt;
FOR i IN 1..tbl_state.count loop
IF tbl_state(i) IS NOT NULL THEN
LV_CNT_NUM := LV_CNT_NUM + 1;
dbms_output.put_line(tbl_state(i));
END IF;
END LOOP;
dbms_output.put_line('That''s it folks');
END;
tbl_state is a table of strings indexed by strings - passing in index values 1, 2, 3 (numbers) won't work.
It is true that array pairs are still ordered (first, second etc.), but accessing them in a loop is a bit more complicated. You will need a WHILE loop, and the index (I kept the name i to match your code as closely as possible) must be declared to be the same data type and length as the keys in the array.
DECLARE
TYPE type_state IS TABLE OF VARCHAR(50)
INDEX BY VARCHAR2(50);
tbl_state type_state;
lv_statecity1_txt VARCHAR2(30):= 'TAMPA';
lv_statecity2_txt VARCHAR2(30):= 'ATLANTA';
lv_statecity3_txt VARCHAR2(30):= 'NYC';
lv_cnt_num NUMBER(5) := 0; -- WHAT IS THIS FOR? NEEDED??
i varchar2(50); -- Notice this line
BEGIN
tbl_state('FLORIDA') := lv_statecity1_txt;
tbl_state('GEORGIA') := lv_statecity2_txt;
tbl_state('New_York') := lv_statecity3_txt;
i := tbl_state.first; -- And this line
while (i is not null) loop -- And this one
LV_CNT_NUM := LV_CNT_NUM + 1;
dbms_output.put_line(tbl_state(i));
i := tbl_state.next(i); -- And this one
END LOOP;
dbms_output.put_line('That''s it folks');
END;
/

Oracle PL/SQL Bubble-Sorting - ORA-01403: no data found ORA-06512: at line 12

Here is the code:
declare
p_arr dbms_sql.Number_Table;
i pls_integer;
procedure do_sort(p_arr in out dbms_sql.Number_Table, p_asc in boolean default null, p_nulls_last in boolean default null) is
x pls_integer;
p_temp number;
begin
for i in 1..p_arr.COUNT-1
loop
for x in 2..p_arr.COUNT
loop
if p_arr(x) < p_arr(x-1)
then
p_temp := p_arr(x-1);
p_arr(x-1) := p_arr(x);
p_arr(x) := p_temp;
end if;
end loop;
end loop;
return;
end;
begin
p_arr(-1) := 0;
p_arr(0) := -2;
p_arr(1) := 10.1;
p_arr(2) := null;
p_arr(3) := 10.1;
p_arr(4) := -1;
do_sort(p_arr);
i := p_arr.first;
while i is not null loop
dbms_output.put_line('arr('||i||') = '||nvl(to_char(p_arr(i)), 'null')||';');
i := p_arr.next(i);
end loop;
end;
It gives me an error on line 12 - "No data found".
Respectively, the procedure "do_sort" on line 29 also fails.
Seems like the problem with nested loop, which I can't figure out for now.
When there is only "first-level" loop with some code in it, such as assigning new values to collection - it performs well.
Sorting block outside of procedure body also works.
Thanks in advance.
You're filling your number table with specific indexes:
p_arr(-1) := 0;
p_arr(0) := -2;
p_arr(1) := 10.1;
p_arr(2) := null;
p_arr(3) := 10.1;
p_arr(4) := -1;
But when you loop you are using index values from 1 to the count of elements, which is 6. There is no element with index 5 or 6, and when you try to refer to p_arr(5) there is no such element - hence the error. And you miss out those with indexes -1 and 0.
It works if you re-index your initial values:
p_arr(1) := 0;
p_arr(2) := -2;
p_arr(3) := 10.1;
p_arr(4) := null;
p_arr(5) := 10.1;
p_arr(6) := -1;
which then gets output:
arr(1) = -2;
arr(2) = 0;
arr(3) = 10.1;
arr(4) = null;
arr(5) = -1;
arr(6) = 10.1;
PL/SQL procedure successfully completed.
Notice what happens with your null value though... you cannot compare null with anything else, so p_arr(x) < p_arr(x-1) is undefined (but not true) when the null element is evaluated on both sides of that comparison. So, it doesn't move. You would need to decide where you want nulls to end up to determine how to modify the code to achieve that.
If you have a specific reason for starting your indexing at -1 instead of 1, you could still do that, and change the references inside your loop to be p_arr(x+2) etc., but it would be more confusing and error-prone. Or you coudl change your loop ranges to handle that instead:
for i in -1..p_arr.COUNT - 1 -- 2 less than previously, on each end of range
loop
for x in 0..p_arr.COUNT - 2 -- 2 less than previously, on each end of range
loop
... which gets the same result, using your original table population starting from index -1. Oracle Live SQL demo.
The indexes used inside the loops have to align with the indexes you use to populate the table, however you do it.

Resources