PL/SQL -If exists in Table type of varchar2 not working - oracle

I am using a huge table which loops inside a cursor so i thought rather than querying on each iteration, put the particular data in a Table type and then check data exists inside the loop.
declare
type type_product_list is table of varchar(6);
product_list type_product_list;
begin
SELECT distinct(PRODUCT_NUMBER)
BULK COLLECT INTO product_list
FROM WEB_PRODUCTS web WHERE some conditions;
FOR i IN 1..product_list.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(product_list(i)); -- This line printing properly.
END LOOP;
IF product_list.EXISTS('00029') THEN -- This condition always fails
DBMS_OUTPUT.PUT_LINE('Found');
ELSE
DBMS_OUTPUT.PUT_LINE('Not Found');
END IF;
end;
Output
00029
00030
00031
00032
..... other data
NOT FOUND
Please help, how can I get the IF block executed.
*Update
Main purpose of this problem is to call a function inside the IF block, and if block itself will be inside a cursor loop which gives dynamic product id in each iteration, i.e. if the product id exists then call that function.
begin
SELECT distinct(PRODUCT_NUMBER)
BULK COLLECT INTO product_list
FROM WEB_PRODUCTS web WHERE some conditions;
OPEN cur_cms_scriptdtl();
LOOP
FETCH cur_cms_scriptdtl INTO productId, productName;
EXIT WHEN cur_cms_scriptdtl%notfound;
IF product_list.EXISTS(productId) THEN
-- Function call
END IF;
END LOOP;
CLOSE cur_cms_scriptdtl;

After some digging and help from Oracle docs, I was able to get it without FOR LOOP.
The helping angel here is : member of
BEGIN
SELECT distinct(PRODUCT_NUMBER)
BULK COLLECT INTO product_list
FROM WEB_PRODUCTS web WHERE some conditions;
OPEN cur_cms_scriptdtl();
LOOP
FETCH cur_cms_scriptdtl INTO productId, productName;
EXIT WHEN cur_cms_scriptdtl%notfound;
IF productId member of product_list THEN
-- Function call
END IF;
END LOOP;
CLOSE cur_cms_scriptdtl;

In collections, exists method receives as parameter an index, not a value.
IF product_list(i) = '00029' THEN
Try this:
DECLARE
type type_product_list is table of varchar(6);
product_list type_product_list;
vFound BOOLEAN := false;
BEGIN
FOR i IN 1..product_list.COUNT LOOP
dbms_output.put_line(product_list(i));
IF product_list(i) = '00029' THEN
vFound := true;
exit;
END IF;
END LOOP;
IF vFound THEN
dbms_output.put_line('Found');
ELSE
dbms_output.put_line('Not Found');
END IF;
END LOOP;

Related

inserting data select into with loop in plsql

I am just want to enter forms detail column data into a database column from a detail block and that must check first entered data which it already have in that column when i entered data data saves in other tables but in token_staus is not entering data and message me No data found for that i write a loop which is not working for me i am making some kind of mistake obviously but not sure where it is
DECLARE
TOKEN_NO NUMBER;
TOKEN_STATUS1 NUMBER;
--TOKEN_STATUS2 := :TOKEN_STATUS;
BEGIN
SELECT SR_NO, TOKEN_STATUS INTO TOKEN_NO, TOKEN_STATUS1 FROM LOOPT2 WHERE SR_NO = :TOKEN_NO;
--IF :TOKEN_STATUS IS NULL
--THEN
LOOP
GO_BLOCK('TOKEN_REC2');
FIRST_RECORD;
INSERT INTO LOOPT2(TOKEN_STATUS) VALUES(:TOKEN_STATUS);
NEXT_RECORD;
EXIT WHEN :SYSTEM.LAST_RECORD = 'TRUE';
END LOOP;
--END IF;
EXCEPTION
when others then
message (sqlerrm);
END;
Please help me
Little Foot please
If I understood you correctly, it would be something like this (check comments within the code): key thing here seems to be a BEGIN-EXCEPTION-END block within the LOOP.
DECLARE
TOKEN_NO NUMBER;
TOKEN_STATUS1 NUMBER;
BEGIN
GO_BLOCK('TOKEN_REC2');
FIRST_RECORD;
LOOP
BEGIN
SELECT SR_NO, TOKEN_STATUS
INTO TOKEN_NO, TOKEN_STATUS1
FROM LOOPT2
WHERE SR_NO = :TOKEN_NO;
-- if such a :TOKEN_NO exists, that SELECT will return some values
-- which means that you want to skip it, so - don't do anything
EXCEPTION
-- if such a :TOKEN_NO does not exist, SELECT will return
-- NO_DATA_FOUND which means that you want to perform insert
WHEN NO_DATA_FOUND THEN
INSERT INTO LOOPT2 (TOKEN_STATUS)
VALUES (:TOKEN_STATUS);
WHEN TOO_MANY_ROWS THEN
NULL;
END;
EXIT WHEN :SYSTEM.LAST_RECORD = 'TRUE';
NEXT_RECORD;
END LOOP;
END;

String Comparision in If condition in PL/SQL

I have the following code
create or replace procedure deact_user (i_email in varchar2)
as
var1 varchar2(200);
begin
for em_id in (select abc.emai_id from abc)
loop
if (i_email <> em_id) then
dbms_output.put_line('Not working');
else
dbms_output.put_line('Working');
end if;
end loop;
end;
I need to compare the i_email which is a input parameter with em_id which is a for loop which loops the table abc having field as emai_id.
Iam facing error PLS=00306 wrong type of arguments in call to '!='
Please help
When you use a for loop with select, its creates a record type. To access the value, you have to change your if to this:
if (I_email <> em_id.emai_id)
.....
That should solve your problem. Now, on the other hand, it would be quicker (and easier) to just query with a where clause using your variable. (that way, you wouldn't need a for loop).
I recommend you to use Cursors for read content and compare values like this:
create or replace procedure deact_user (i_email in varchar2)
as
var_id varchar2(200);
cursor cur_em_id is select abc.emai_id from abc;
begin
open cur_em_id;
loop
fetch cur_em_id into var_id;
exit when cur_em_id%NOTFOUND;
if (i_email <> var_id) then
dbms_output.put_line('Not working');
else
dbms_output.put_line('Working');
end if;
end loop;
close cur_em_id;
end;

Oracle Function that return table of number problems and performance

I have (sometimes) a memory block in my oracle database that turn crasy... a lot of session sundenless block each other and the probleme is in a function that return a table of number and is use in another procedure.
Edit : Sessions is 'blocked' with Read By Other Session Wait Event
First, my table of number :
CREATE OR REPLACE TYPE liste_lots as TABLE OF number(10)
and in large the function who populate the table :
function get_ot_idem_cursor( .. ) return liste_lots is
res_type liste_lots;
p_restriction_level number;
cursor curs_lvl_1 is select [...] ;
row_lvl_1 curs_lvl_1%rowtype;
cursor curs_lvl_0 is select [...] ;
row_lvl_0 curs_lvl_0%rowtype;
begin
res_type := liste_lots();
p_restriction_level := get_edi_line_restriction(p_edi_line);
if p_restriction_level = 1 then
open curs_lvl_1;
loop
fetch curs_lvl_1 into row_lvl_1;
exit when curs_lvl_1%notfound;
begin
res_type.extend;
res_type(res_type.last) := row_lvl_1.lot_id;
exception
when others then
dbms_output.put_line('problème get_ot_idem_cursor ');
dbms_output.put_line(sqlerrm);
close curs_lvl_1;
end;
end loop;
close curs_lvl_1;
else
open curs_lvl_0;
loop
fetch curs_lvl_0 into row_lvl_0;
exit when curs_lvl_0%notfound;
begin
res_type.extend;
res_type(res_type.last) := row_lvl_0.lot_id;
exception
when others then
dbms_output.put_line('problème get_ot_idem_cursor ');
dbms_output.put_line(sqlerrm);
close curs_lvl_0;
end;
end loop;
close curs_lvl_0;
end if;
return res_type;
exception
when others then
if curs_lvl_0%isopen then
close curs_lvl_0;
end if;
if curs_lvl_1%isopen then
close curs_lvl_1;
end if;
end;
and is used on another part like that :
liste_ots := get_ot_idem_cursor(v_lot, v_sr_ligne_lot.id );
select min(l.lot_id) into result
from lot l
where l.des_tiers_id = p_pf_tiers_id
and l.lot_nature = 'POS'
and l.exp_tiers_id = v_sr_ligne_lot.ramasse_tiers_id
and ot_id in ((select * from TABLE(liste_ots)))
and l.lot_datheurcharg > sysdate - 3;
When the db become crasy (session block, very slow) this is the part of the code who is pointed :
select * from TABLE(liste_ots)
the problem is not all the time, then if you have any idea or advise...
thanks in advance (sorry for my bad english)
Use a bulk collect instead of a plain loop (fetching records one by one) as there is no additional logic done in the loop. In general, always avoid switching a context (SQL to/from PL/SQL)
open curs_lvl_1;
fetch curs_lvl_1 bulk collect into res_type;
close curs_lvl_1;

PL/SQL - Pre actions if cursor is found

I have a simple cursor like this:
CURSOR emp_cur
IS
SELECT *
FROM employee
WHERE age > 20;
In my procedure, I want to do some pre-actions only if there are employee in cursor. After that pre-action, I process all rows.
I need this because only if exists employee in that cursor i need to cleanup some tables, otherwise i should "RETURN".
so the code could be:
OPEN emp_cur;
/* Here i need to do pre-action only if emp_cur has rows*/
IF /* has rows*/
THEN
/* do some actions*/
END IF;
LOOP
FETCH emp_cur INTO emp_rec;
EXIT WHEN emp_cur%NOTFOUND;
END LOOP;
CLOSE emp_cur;
For now, i have a "dirty" solution where i open cursor:
First to check if there are rows
Do pre-action and close
Open/fetch again to process rows, and close again
First to check if there are rows
You cannot know about the rows until you FETCH.
From documentation link ,
After a cursor or cursor variable is opened but before the first
fetch, %FOUND returns NULL. After any fetches, it returns TRUE if the
last fetch returned a row, or FALSE if the last fetch did not return a
row.
Once you have fetched the rows, then before processing the rows, you could use %FOUND.
For example,
OPEN c1;
LOOP
FETCH c1 INTO my_ename, my_salary;
IF c1%FOUND THEN -- fetch succeeded
-- Do something
ELSE -- fetch failed, so exit loop
EXIT;
END IF;
END LOOP;
Thinking a little i've wrote this procedure that avoid an IF inside the loop. I know, is a little "strange" but is the only thing i've think works:
OPEN emp_cur;
FECTH emp_cur INTO emp_rec;
IF emp_cur%FOUND
THEN
-- pre actions
END IF;
LOOP
EXIT WHEN emp_cur%NOTFOUND;
-- do something in the loop
FECTH emp_cur INTO emp_rec; -- First fetch was done before the if
END LOOP;
CLOSE emp_cur;

Are there alternative methods for saying 'next' in a pl/sql for loop?

So I've got a for loop that processes a list of IDs and has some fairly complex things to do. Without going into all the ugly details, basically this:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
...snip...
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
-- do some data checking stuff...
-- here we will look for duplicate entries, so we can noop if duplicate is found
BEGIN
SELECT county_id INTO v_dup_check FROM org_county_accountable
WHERE organization_id = :P4_ID AND county_id = v_county_id;
-- NEXT;! NOOP;! but there is no next!
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('no dups found, proceeding');
END;
-- here we have code we only want to execute if there are no dupes already
IF v_dup_check IS NULL THEN
-- if not a duplicate record, proceed...
ELSE
-- reset duplicate check variable
v_dup_check := NULL;
END;
END LOOP;
END;
How I normally handle this is by selecting into a value, and then wrap the following code in an IF statement checking to make sure that duplicate check variable is NULL. But it's annoying. I just want to be able to say NEXT; or NOOP; or something. Especially since I already have to catch the NO_DATA_FOUND exception. I suppose I could write a letter to Oracle, but I'm curious how others handle this.
I could also wrap this in a function, too, but I was looking for something a little cleaner/simpler.
Oracle 11g adds a C-style "continue" loop construct to PL/SQL, which syntactically sounds like what you're looking for.
For your purposes, why not just eliminate the duplicates prior to entering the loop? This could be done by querying l_selected using a table function, and then filtering out records you don't want instead of iterating over every value. Something like...
declare
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
cursor no_dups_cur (p_selected APEX_APPLICATION_GLOBAL.VC_ARR2) is
select * from (
select selected.*,
count(*) over (partition by county_id) cnt -- analytic to find counts grouped by county_id
from table(p_selected) selected -- use table function to treat VC_ARR2 like a table
) where cnt = 1 -- remove records that have duplicate county_ids
;
begin
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
for i in no_dups_cur(l_selected) loop
null; -- do whatever to non-duplicates
end loop;
end;
Just substitute the logic for determining a "duplicate" with your own (didn't have enough info from your example to really answer that part)
Instead of catching NO_DATA_FOUND, how about SELECTing the number of matching entries into a variable, say l_count, and proceeding if this count works out to be zero? Something like the following:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
l_count INTEGER;
...snip...
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
-- do some data checking stuff...
-- here we will count duplicate entries, so we can noop if duplicate is found
SELECT COUNT(*) INTO l_count FROM org_county_accountable
WHERE organization_id = :P4_ID AND county_id = v_county_id;
IF l_count = 0 THEN
-- here we have code we only want to execute if there are no dupes already
-- if not a duplicate record, proceed...
END IF;
END LOOP;
END;
To count the number of rows is also possible (see Pourquoi Litytestdata) but you can also do what you want to do in the when_no_data_found exception block.
declare
l_selected apex_application_global.vc_arr2;
l_county_id org_county_accountable.count_id%type;
begin
l_selected := apex_util.string_to_table(:p4_select_lst);
for i in l_selected.first..l_selected.last loop
begin
select count_id
into l_county_id
from org_county_accountable
where organization_id = :p4_id
and county_id = v_county_id;
exception
when no_data_found then
-- here we have code we only want to execute if there are no dupes already
-- if not a duplicate record, proceed...
end;
end loop;
end;
<xmp>
<<next_loop>>
loop
...
...
if ....
then
goto next_loop;
</xmp>
This is a case where a GOTO statement might be useful. See the Oracle Documentation in the control structures to see how to do this. Also, you may want to search around here to find out how to query for the existence of a record. Running a query and waiting for an exception isn't optimal.
Another way - turn the check into a local function:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
...snip...
FUNCTION dup_exists
( p_org_id org_county_accountable.organization_id%TYPE
, p_county_id org_county_accountable.county_id%TYPE
) RETURN BOOLEAN
IS
v_dup_check org_county_accountable.county_id%TYPE;
BEGIN
SELECT county_id INTO v_dup_check FROM org_county_accountable
WHERE organization_id = p_org_id AND county_id = p_county_id;
RETURN TRUE;
EXCEPTION WHEN NO_DATA_FOUND THEN
RETURN FALSE;
END;
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
-- do some data checking stuff...
-- here we have code we only want to execute if there are no dupes already
IF NOT dup_exists (:P4_ID, v_county_id) THEN
-- if not a duplicate record, proceed...
END;
END LOOP;
END;
Of course, the local function could be re-written to use the count method if you prefer:
FUNCTION dup_exists
( p_org_id org_county_accountable.organization_id%TYPE
, p_county_id org_county_accountable.county_id%TYPE
) RETURN BOOLEAN
IS
l_count INTEGER;
BEGIN
SELECT COUNT(*) INTO l_count
FROM org_county_accountable
WHERE organization_id = p_org_id AND county_id = p_county_id;
RETURN (l_count > 0);
END;
Another method is to raise and handle a user-defined exception:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
duplicate_org_county EXCEPTION;
...snip...
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
BEGIN
-- do some data checking stuff...
-- here we will look for duplicate entries, so we can noop if duplicate is found
BEGIN
SELECT county_id INTO v_dup_check FROM org_county_accountable
WHERE organization_id = :P4_ID AND county_id = v_county_id;
RAISE duplicate_org_county;
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('no dups found, proceeding');
END;
-- here we have code we only want to execute if there are no dupes already
EXCEPTION
WHEN duplicate_org_county THEN NULL;
END;
END LOOP;
END;
I wouldn't normally do this, but if there were half a dozen reasons to jump to the next record, this might be preferable to multiple nested IFs.
I know this is an oldie but I couldn't help notice that none of the answers above take into account the cursor attributes:
There are four attributes associated with cursors: ISOPEN, FOUND, NOTFOUND, and ROWCOUNT. These attributes can be accessed with the % delimiter to obtain information about the state of the cursor.
The syntax for a cursor attribute is:
cursor_name%attribute
where cursor_name is the name of the explicit cursor.
So in this case you could use ROWCOUNT (which indicates the number of rows fetched so far) for your purposes, like this:
declare
aux number(10) := 0;
CURSOR cursor_name is select * from table where something;
begin
select count(*) into aux from table where something;
FOR row IN cursor_name LOOP
IF(aux > cursor_name%ROWCOUNT) THEN 'do something is not over';
ELSE 'do something else';
END IF;
END LOOP;
end;

Resources