Why I cant delete element from collection (array) PLSQL - oracle

At this handler I delete NULL element:
if p_arr(i) is null then
p_arr.delete(p_arr(i));
end if;
but null element still in collection. How can I correct this?
The task is to sort elements in collection, but the condition is that there can be NULL in collection. I should delete them.
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 - 2
loop
if p_arr(i) is null then
p_arr.delete(p_arr(i));
end if;
end loop;
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;

That's because you are passing the element itself as the argument to the delete, rather than the index
p_arr.delete(p_arr(i)) is equivalent to p_arr.delete(NULL) - So it simply doesn't delete anything.
So, change it to p_arr.DELETE(i) it will work.

Related

Comparing data of any type without overloading

let's start with an example:
type Collection_t is table of varchar2(3);
Collection_v Collection_t := Collection_t('qwe', 'asd', 'yxc', 'rtz', 'fgh', 'vbn');
single_var varchar2(3) := 'yxc';
procedure get_position(Single_Value varchar2, Value_Set Collection_t) is
i integer := 1;
begin
while (i is not null) loop
if Single_Value = Value_Set(i)
then
dbms_output.put_line(i);
end if;
i := Value_Set.Next(i);
end loop;
end;
begin
get_position(single_var, Collection_v);
end;
now, question is: can I declare this procedure with 'anydata', and check if table (2ed argument) consists of the same type as first argument.
I'd assume declaration of a procedure would look like that:
procedure get_position(Single_Value anydata, Value_Set anydata) is ...
later I'd compare types, and honestly I couldn't figure it out, how to solve this problem.
From my limited understanding, you still have to encode/decode anydata into basic (or user-defined) PL/SQL types to do anything meaningful like comparing values, so you might not gain the benefits of dynamic languages like Python.
Here is an example, passing anydata as a parameter. You'll need to update anydata_to_varchar to handle data types you need.
Personally, overloading seems like a more straightforward approach, unless there is a better way to work with anydata values.
declare
type tab_anydata is table of anydata;
-- test as varchar2
/*
lookup_array tab_anydata := tab_anydata(
anydata.convertVarchar2('blah'),
anydata.convertVarchar2('meh'),
anydata.convertVarchar2('foo'),
anydata.convertVarchar2('bar')
);
lookup_value anydata := anydata.convertVarchar2('foo');
*/
-- test as date
lookup_array tab_anydata := tab_anydata(
anydata.convertDate(trunc(sysdate - 2)),
anydata.convertDate(trunc(sysdate - 0)),
anydata.convertDate(trunc(sysdate + 1)),
anydata.convertDate(trunc(sysdate + 2))
);
lookup_value anydata := anydata.convertDate(trunc(sysdate));
function anydata_to_varchar(p_what anydata)
return varchar2
is
value_type varchar2(30) := anydata.GetTypeName(p_what);
result varchar2(32767);
begin
select
case value_type
when 'SYS.VARCHAR2' then anydata.AccessVarchar2(p_what)
when 'SYS.DATE' then to_char(anydata.AccessDate(p_what), 'YYYY-MM-DD')
when 'SYS.NUMBER' then to_char(anydata.AccessNumber(p_what))
else null
end into result
from dual;
return result;
end;
function get_position(p_what anydata, p_where tab_anydata)
return number
is
pos number := -1;
what_value varchar2(30) := anydata_to_varchar(p_what);
curr_value varchar2(30);
begin
for i in 1..p_where.count
loop
curr_value := anydata_to_varchar(p_where(i));
if what_value = curr_value then
pos := i;
exit;
end if;
end loop;
return pos;
end;
begin
dbms_output.put_line('lookup type : '||anydata.GetTypeName(lookup_value));
dbms_output.put_line('lookup value : '||anydata_to_varchar(lookup_value));
dbms_output.put_line('found position: '||get_position(lookup_value, lookup_array));
end;
/
dbms_output:
lookup type : SYS.DATE
lookup value : 2022-07-20
found position: 2
db<>fiddle here

Oracle Reference to uninitialized composite while passing object as output parameter PL/SQL

I have a problem with "ORA-06530: Reference to uninitialized composite". The object is as far as I understand initialiazed.
CREATE OR REPLACE TYPE t_RetVal AS OBJECT
(
var_n_retVal NUMBER,
var_n_sqlcode NUMBER,
var_v_sqlerrm VARCHAR(255),
CONSTRUCTOR FUNCTION t_RetVal(SELF IN OUT NOCOPY t_RetVal) RETURN SELF AS RESULT
);
/
CREATE OR REPLACE TYPE BODY t_RetVal AS
CONSTRUCTOR FUNCTION t_RetVal(SELF IN OUT NOCOPY t_RetVal) RETURN SELF AS RESULT IS
BEGIN
SELF.var_n_retVal := 1;
SELF.var_n_sqlcode := 0;
SELF.var_v_sqlerrm := '';
RETURN;
END;
END;
create or replace FUNCTION get_Something(
id IN NUMBER,
out_t_RetVal OUT NOCOPY t_RetVal
)
RETURN NUMBER
IS
retVal NUMBER := 1;
BEGIN
out_t_RetVal.var_n_retVal:= 1;
out_t_RetVal.var_n_sqlcode := 0;
out_t_RetVal.var_v_sqlerrm := '';
RETURN out_t_RetVal;
END;
In the procedure i do the following and keep getting "ORA-06530: Reference to uninitialized composite".
create or replace procedure PROC is
var_t_SubFuncRetVal t_RetVal;
v_n_retVal number := 0;
v_error_text varchar2(1000);
BEGIN /*PROCEDURE START*/
var_t_SubFuncRetVal := t_RetVal();
v_n_retVal := get_Something( id, var_t_SubFuncRetVal);
IF (var_t_SubFuncRetVal.var_n_retVal >= 0) THEN
IF (var_t_SubFuncRetVal.var_n_retVal = 0) THEN
....
ELSE
....
END IF;
ELSE
v_error_text := 'Error message is '|| 'SQLCODE: '||var_t_SubFuncRetVal.var_n_sqlcode||' -- ERROR: '||var_t_SubFuncRetVal.var_v_sqlerrm;
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END PROC;
Why is Oracle reporting this error ? How to properly pass object as out parameter ?

Wrong number or TYPES of arguments, error in PL/SQL

I have to create a list of RECORD and I need to send it to a procedure.
There is my header.
CREATE OR REPLACE PACKAGE tema4 IS
TYPE obj IS RECORD(id INTEGER := 0,percent INTEGER := 0);
TYPE listObj IS TABLE OF obj INDEX BY PLS_INTEGER;
PROCEDURE ex1 (p_listObj IN listObj);
END tema4;
My body.
create or replace PACKAGE BODY tema4 IS
PROCEDURE ex1 (p_listObj IN listObj) IS
BEGIN
DBMS_OUTPUT.PUT_LINE('Cant reach');
END ex1;
END tema4;
And my code that calls procedure ex1.
DECLARE
TYPE obj IS RECORD(id INTEGER := 0,percent INTEGER := 0);
TYPE listObj IS TABLE OF obj INDEX BY PLS_INTEGER;
v_obj obj;
v_listObj listObj;
BEGIN
FOR v_i IN (SELECT ID,BURSA FROM STUDENTI ORDER BY ID) LOOP
v_obj.id := v_i.id;
v_obj.percent := 50;
v_listObj(v_i.id) := v_obj;
END LOOP;
FOR v_i IN v_listObj.FIRST..v_listObj.LAST LOOP
DBMS_OUTPUT.PUT_LINE(v_listObj(v_i).id || ' - ' ||
v_listObj(v_i).percent);
END LOOP;
tema4.ex1(v_listObj); --this line is with problems
END;
PLS-00306: wrong number or types of arguments in call to 'EX1'
Can someone explain me what is wrong in my code? I also tried to create my type as global, but it won't let me because of 'RECORD' keyword.
Don't declare the types again (new types), use the types already declared in the package spec:
DECLARE
v_obj tema4.obj;
v_listObj tema4.listObj;
BEGIN
FOR v_i IN (SELECT ID,BURSA FROM STUDENTI ORDER BY ID) LOOP
v_obj.id := v_i.id;
v_obj.percent := 50;
v_listObj(v_i.id) := v_obj;
END LOOP;
FOR v_i IN v_listObj.FIRST..v_listObj.LAST LOOP
DBMS_OUTPUT.PUT_LINE(v_listObj(v_i).id || ' - ' ||
v_listObj(v_i).percent);
END LOOP;
tema4.ex1(v_listObj); --this line is with problems
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