loop counter case in oracle procedure - oracle

Is there any loop counter in procedure.
How to handle below case.
BEGIN
FOR recAnnLang IN getLang(var_non_subscribe)
LOOP
// if loop 1 recAnnLangCode.Ann_Lang assign to var1
var1 := recAnnLangCode.Ann_Lang;
// if loop 2 recAnnLangCode.Ann_Lang assign to var2
var2 := recAnnLangCode.Ann_Lang;
// if loop 3 recAnnLangCode.Ann_Lang assign to var2
var3 := recAnnLangCode.Ann_Lang;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Debug :: Inside Exception because data not found ' );
END;

DECLARE n_counter NUMBER := 0;
BEGIN
LOOP
n_counter := n_counter + 1;
DBMS_OUTPUT.PUT_LINE(n_counter);
IF n_counter = 5 THEN
EXIT;
END IF;
END LOOP;
END;
I did it like this.

maybe you are looking for something like
declare
type a_type is table of recAnnLang.Ann_Lang%type index by pls_integer;
val a_type;
begin
for recAnnLang in getLang(var_non_subscribe) loop
val(nvl(val.last, 0) + 1) := recAnnLang.Ann_Lang;
end loop;
for i in 1 .. val.last loop
dbms_output.put_line(val(i));
end loop;
end;

maybe,if getlang is an explicit cursor,you may think of using rownum in the cursor query select clause and use it as the counter.

Related

PLSQL - 2 FOR loops on nested tables

PLSQL - 2 FOR loops on nested tables, one after the other, the second loop isn't performed while the nested table of the first loop is empty.
Follow is the code snippet:
type prod_seq_array is table of wiz_customer_hp_product.product_seq%type;
c_prod_add_arr prod_seq_array := prod_seq_array ();
c_prod_drop_arr prod_seq_array := prod_seq_array (1234,5678);
FOR i IN c_prod_add_arr.FIRST .. c_prod_add_arr.LAST LOOP
mydebug2.debug_out(fp,'shira2 ' ||wo_prod_rec_1.product_seq);
if v_prod_seq = c_prod_add_arr(i) then
v_status := 'C';
ins_hp_prod_svc;
v_drop_wo_date := v_add_date;
exit;
end if;
END LOOP;
FOR i IN c_prod_drop_arr.FIRST .. c_prod_drop_arr.LAST LOOP
if v_prod_seq = c_prod_drop_arr(i) then
v_status := 'C';
del_hp_prod_svc_hist ;
v_drop_wo_date := v_drop_date;
exit;
end if;
END LOOP;
I understand that FOR loop on empty nested table will throw exception so I added the following condition before each FOR loop
if c_prod_add_arr.count > 0
FOR i IN c_prod_add_arr.FIRST .. c_prod_add_arr.LAST LOOP
if v_prod_seq = c_prod_add_arr(i) then
v_status := 'C';
ins_hp_prod_svc;
v_drop_wo_date := v_add_date;
exit;
end if;
END LOOP;
end if;

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;

How to built a dynamic string PLSQL

I have 2 associative_arrays of number and I am trying to built a dynamic string for a dynamic query.
This is my for statement:
for indx_opt in 1..IDOPTarray.count loop
IF indx_opt=1 AND IDFGCFCParray.count=1 THEN
sql_stmt_2:=sql_stmt_2||' and wopt.id_ft_opt = ';
sql_stmt_2:=sql_stmt_2|| (IDOPTarray(indx_opt));
end if;
if indx_opt=1 AND IDFGCFCParray.count>1 then
sql_stmt_2:=sql_stmt_2||' and wopt.id_ft_opt in(';
sql_stmt_2:=sql_stmt_2||(IDOPTarray(indx_opt));
elsif indx_opt>=1 AND IDFGCFCParray.count>=0 then
sql_stmt_2:=sql_stmt_2||','||(IDOPTarray(indx_opt))||')';
With 2 number in IDOPTarray I get a correct result:
and wopt.id_ft_opt in(27,28)
Instead with more then 2 number in IDOPTarray I get this result:
,17228),17229),17230)
What I want to get is this:
where w.id = 303 and wopt.id_ft_opt in (17228,17229,17230)
if I have 5 numbers, I want to get this for the 'where' clause:
where w.id = 321 and wopt.id_ft_opt in (17228,17229,17230,17231,17232)
I want a dynamic output of my string.
IDFGCFCParray is the 2nd array, but is not important right now in order to get the output I want.
Does somebody can help me? thank you.
You have to close bracket only if indx_opt is equil to IDOPTarray.count.
And simple example.
declare
type list_number is table of number;
xx list_number := new list_number(1,2,3,5,7,8);
str varchar2(4000);
begin
for i in xx.first .. xx.last loop
if i = 1 then
str := ' condition in ('||xx(i);
else
str := str||','||xx(i);
end if;
if i = xx.last then
str := str||')';
end if;
end loop;
dbms_output.put_line(str);
end;
If you colleciton is sql levle type you can do this in this way
declare
xx list_number := new list_number(1,2,3,5,7,8);
str varchar2(4000);
begin
SELECT 'condition in ('||LISTAGG(column_value, ',') WITHIN GROUP (ORDER BY column_value)||')' into str from table(xx);
dbms_output.put_line(str);
end;

ORACLE - How to count the number of occurrences of a member in a collection?

If I had a collection in Oracle like this:
DECLARE
TYPE v_type IS TABLE OF varchar2(4000);
v_coll v_type := v_type();
v_num_dups NUMBER;
BEGIN
v_coll.extend();
v_coll(1):= 'not dupe';
v_coll.extend();
v_coll(2):= 'dupe';
v_coll.extend();
v_coll(3):= 'dupe';
for i in 1..v_coll.count loop
dbms_output.put_line(v_coll(i));
end loop;
END;
How could I return the count of dupe occurrences into v_num_dups without looping through the collection and counting?
Is there some simple way to say v_coll.count('dupe') or something like that?
If you wanted to avoid duplicates entirely, you could use an associative array. Something like:
DECLARE
TYPE v_type IS TABLE OF number index by varchar2(4000);
v_coll v_type;
idx varchar2(4000);
BEGIN
v_coll('not dupe'):= 1;
v_coll('dupe'):= 1;
v_coll('dupe'):= 1;
-- show key values
idx := v_coll.first;
loop
exit when idx is null;
dbms_output.put_line(idx);
idx := v_coll.next(idx);
end loop;
END;
Output:
dupe
not dupe
Unfortunately, there isn't (at least as of 11g).
You have to iterate through the collection manually and perform the count.
While I like tbone's answer, this also works VERY nice to remove duplicates:
DECLARE
TYPE v_type IS TABLE OF varchar2(4000);
v_coll v_type := v_type();
v_num_dups NUMBER;
BEGIN
v_coll.extend();
v_coll(1):= 'not dupe';
v_coll.extend();
v_coll(2):= 'dupe';
v_coll.extend();
v_coll(3):= 'dupe';
v_coll:=SET(v_coll); --<----remove dups!
dbms_output.put_line(cardinality(v_coll));
for i in 1..v_coll.count loop
dbms_output.put_line(v_coll(i));
end loop;
END;

How to loop through a delimited list in Oracle PLSQL

I am working on an Oracle procedure that calls another procedure within it. One of my parameters (parm1) can contain one or more values in a comma separated list. How can I loop through these values to pass them one at a time to another procedure?
Here is an example of what I would like it to do:
When Parm1 = 123,312
callProcedure2(123)
callProcedure2(321)
-or-
When Parm1 123
callProcedure2(123)
I think this can be accomplished using a loop but I can't figure out how to get it to use each value as a separated call within the loop.
Any help would be appreciated!
Thanks!
CURSOR V_CUR IS
select regexp_substr(Parm1 ,'[^,]+', 1, level) As str from dual
connect by regexp_substr(Parm1, '[^,]+', 1, level) is not null;
This curor will give you result like this
123
321
Now iterate the cursor and call the procedure in loop.
For i IN V_CUR
LOOP
callProdcedure2(i.str);
END LOOP;
Just loop through substrings:
declare
parm1 varchar2(1000) := '123,234,345,456,567,789,890';
vStartIdx binary_integer;
vEndIdx binary_integer;
vCurValue varchar2(1000);
begin
vStartIdx := 0;
vEndIdx := instr(parm1, ',');
while(vEndIdx > 0) loop
vCurValue := substr(parm1, vStartIdx+1, vEndIdx - vStartIdx - 1);
-- call proc here
dbms_output.put_line('->'||vCurValue||'<-');
vStartIdx := vEndIdx;
vEndIdx := instr(parm1, ',', vStartIdx + 1);
end loop;
-- Call proc here for last part (or in case of single element)
vCurValue := substr(parm1, vStartIdx+1);
dbms_output.put_line('->'||vCurValue||'<-');
end;
There is a utility procedure COMMA_TO_TABLE and array type DBMS_UTILITY.UNCL_ARRAY dedicated for this task. Since Oracle 10g.
It is well document here.
Here is a sample solution:
SET SERVEROUTPUT ON
DECLARE
csvListElm VARCHAR2(4000) := 'elm1, elm2,elm3 ,elm4 , elm5';
csvListTable DBMS_UTILITY.UNCL_ARRAY;
csvListLen BINARY_INTEGER;
currTableName VARCHAR2(222);
BEGIN
DBMS_UTILITY.COMMA_TO_TABLE(csvListElm, csvListLen, csvListTable);
FOR csvElm IN 1..(csvListTable.COUNT - 1) LOOP
dbms_output.put_line('-- CSV element : <'||csvListTable(csvElm)||'>');
dbms_output.put_line('-- Trimmed CSV element: <'||trim(csvListTable(csvElm))||'>');
END LOOP;
END;
/
Sample output:
-- CSV element : <elm1>;
-- Trimmed CSV element: <elm1>;
-- CSV element : < elm2>;
-- Trimmed CSV element: <elm2>;
-- CSV element : <elm3 >;
-- Trimmed CSV element: <elm3>;
-- CSV element : <elm4 >;
-- Trimmed CSV element: <elm4>;
-- CSV element : < elm5>;
-- Trimmed CSV element: <elm5>;
It is possible to use a function that you can use in a for loop (without regexp for ThinkJet):
Create a type and function
CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
FUNCTION cto_table(p_sep in Varchar2, p_list IN VARCHAR2)
RETURN t_my_list
AS
l_string VARCHAR2(32767) := p_list || p_sep;
l_sep_index PLS_INTEGER;
l_index PLS_INTEGER := 1;
l_tab t_my_list := t_my_list();
BEGIN
LOOP
l_sep_index := INSTR(l_string, p_sep, l_index);
EXIT
WHEN l_sep_index = 0;
l_tab.EXTEND;
l_tab(l_tab.COUNT) := TRIM(SUBSTR(l_string,l_index,l_sep_index - l_index));
l_index := l_sep_index + 1;
END LOOP;
RETURN l_tab;
END cto_table;
/
Then how to call it in the for loop:
DECLARE
parm1 varchar2(4000) := '123,234,345,456,567,789,890';
BEGIN
FOR x IN (select * from (table(cto_table(',', parm1)) ) )
LOOP
dbms_output.put_line('callProdcedure2 called with ' || x.COLUMN_VALUE);
callProdcedure2(x.COLUMN_VALUE);
END LOOP;
END;
/
Notice the default name COLUMN_VALUE given by Oracle, which is necessary for the use I want to make of the result.
Result as expected:
callProdcedure2 called with 123
callProdcedure2 called with 234
...

Resources