where do i use no data found exception on FOR statement inside a stored procedure - oracle

i have the next question:
Im not very good at DBs, i've been requested to add a "No data found exception" to a stored procedure.
This is the SP:
CREATE OR REPLACE PROCEDURE TABLE."SP_UPD"
(
PERROR OUT VARCHAR2
)
AS
BEGIN
FOR TMP_TABLE IN
(SELECT FIELDS FROM TABLES)
--I need to verify HERE if the for returns--
--no values, because once the loop starts it automaticaly updates tables--
LOOP
BEGIN
CODE
MORE CODE
END
END LOOP;
I added the exception here, at the bottom before the last backslash, is this right?:
EXCEPTION WHEN NO_DATA_FOUND
THEN
perror:='error message';
return;
END;
/

You don't have to do anything, because - in a cursor FOR loop (which is what you have), Oracle will skip everything between LOOP and END LOOP if cursor doesn't return any rows.
So: if select fields from tables doesn't return anything , code and more code won't ever be executed.

Related

handling excpetions in plsql block while running in a loop

I have a requirement where I need to drop partitions for more than one tables in a loop.If for some reason that partition doesnt exist in a table the whole procedure is giving error . But I want to drop other partititions which exists in other tables without coming out of the loop
Use an inner begin-exception-end block ("inner" meaning "within a loop"). Something like this:
begin
for cur_r in (select whatever from ...) loop
-- inner block begins here
begin
do stuff here
exception
when ... then ...
end;
-- inner block ends here
end loop;
end;

Large tiff file is not inserting

I have a function, which has some parameter. One of the parameter is clob where i'm passing a tiff file from front end screen. In front end the tiff data is embedded in xaml and calling the function. I'm trying to store the tiff file into a table. i can able to insert tiff has length less than 32768. When i try to insert more than that its not inserting into the table. I can't even try to find the length of the file. It shows nothing. i have tried put it in bind variable and execute immediate the insert statement. nothing inserted.
CREATE OR REPLACE FUNCTION insert_tiff_data
(
as_fno varchar2(10),
as_a_code IN amc.amc_code%TYPE,
as_app_sign clob,
as_sataus IN VARCHAR2
) RETURN VARCHAR2 IS
--declared variables here
BEGIN
ls_file := as_a_code || '_' || as_fno || '.TIF';
BEGIN
BEGIN
SELECT COUNT(*)
INTO ll_count
FROM tiff_data
WHERE filename = ls_file AND
image_type = 'S' AND
fid = as_fno;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20780,
excep);
ll_count := 0;
END;
IF ll_count >= 1 THEN
RETURN 'Z';
ELSE
IF length(as_app_sign) > 0 THEN
BEGIN
INSERT INTO tiff_data
(filename,
image_type,
amc_code,
fid,
image_date,
image,
editor_id,
xy_cor)
VALUES
(ls_file,
'S',
as_a_code,
as_fno,
SYSDATE,
plf_base64_clob_to_blob(as_app_sign),
'',
'');
IF SQL%ROWCOUNT > 0 THEN
RETURN 'Y';
ELSE
RETURN 'C';
END IF;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20781,
excep);
RETURN 'D';
END;
END IF;
END IF;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20782,
excep);
RETURN 'E';
END;
RETURN 'F';
END insert_tiff_data;
I think function abnormally closed when trying to access that variable as_app_sign.
You need to check out the the Oracle supplied package DBMS_LOB, for processing a large object (blob, clob, ...). If your data exceeds 32767 bytes, you will need to build it up in segments. (32767 is the largest block size that can be transmitted at one time).
You also have a issue with your exception handling. Raise_Application_Error immediately terminates the current block with the error code and message specified, any code following, passing that to the exception block or the exception block of any higher block or out of the procedure, any code after RAE in the same block is not executed. Thus your function is incapable or returning D or E or setting ll_count to 0. Further a return statement executed immediately exits the procedure and no following code is executed. I have not exhaustive validated, but I do not think the current function is capable of returning F either. I have put together a fiddle for demonstration. It patterns itself after your logic (using parameters for values), but probably does not get the logic exactly.

Oracle - two loops in procedure

I need some help in writing Oracle PL/SQL procedure that should do the following:
the procedure is called from a trigger after an update of the field in one table with the input parameter B-block or D-activate (this is already done)
the procedure should first open one cursor that will catch the account numbers of a client and open a loop that will process account by account
this one account should be forwarded to another loop that will catch card numbers of that client for that account (second cursor) and when into this loop, the card number should be used as an input parameter for a stored procedure that is called to block/unblock this card - this stored procedure already exists I just need to call it
the procedure don't need to return any parameters, the idea is just to block/activate card number of a client with the already written stored procedure for that
Should I write a package for this or just a procedure? And how can I write one loop in another?
I just realized that i can do this without cursors in a procedure. For simple example:
create or replace procedure blokiraj_proc (core_cust_id varchar2, kyc_blocked varchar2) as
type NumberArray is Array(100) of test_racuni.foracid%type;
type StringArray is Array (1000) of test_kartice.card_num%type;
accnt NumberArray;
card_number StringArray;
begin
select foracid bulk collect into accnt from test_racuni where cif_id = core_cust_id;
for i in accnt.first..accnt.last
loop
select card_num bulk collect into card_number from test_kartice where rbs_acct_num = accnt(i);
dbms_output.enable (100000);
dbms_output.put_line (accnt(i));
for j in 1..card_number.count
loop
dbms_output.put_line (card_number(j));
blokiraj_karticu (card_number(j));
end loop;
end loop;
end;
Is this a better approach then the curssors? And why is dbms_output not printing anything when i trigger the procedure?
As #EdStevens indicated you cannot avoid processing cursors. But you can avoid the looping structure of cursor within cursor. And the implicit open and close cursor for the inner one. The queries have combine into a simple JOIN then bulk collect into a single collection.
For this I created a RECORD to contain both the account number and card number; then a collection of that record. The cursor is then bulk collected into the collection. Your initial code allows for up to 100000 cards to be processed, and while I am a fan of bulk collect (when needed) I am not a fan of filling memory, therefore I limit the number of rows bulk collect gathers of each fetch. This unfortunately introduces a loop-within-loop construct, but the penalty is not near as great as cursor-within-cursor construct. The following is the result.
create or replace procedure blokiraj_proc (core_cust_id varchar2) as
type acct_card_r
is record(
acct_num test_kartice.rbs_acct_num%type
, card_num test_kartice.card_num%type
);
type acct_card_array is table of acct_card_r;
acct_card_list acct_card_array;
k_acct_card_buffer_limit constant integer := 997;
cursor c_acct_card(c_cust_id varchar2) is
select r.foracid
, k.card_num
from test_racuni r
left join test_kartice k
on (k.rbs_acct_num = r.foracid)
where r.cif_id = c_cust_id
order by r.foracid
, k.card_num;
begin
dbms_output.enable (buffer_size => null); -- enable dbms_output with size unlimited
open c_acct_card(core_cust_id);
loop
fetch c_acct_card
bulk collect
into acct_card_list
limit k_acct_card_buffer_limit;
for i in 1 .. acct_card_list.count
loop
dbms_output.put (acct_card_list(i).acct_num || ' ==> ');
if acct_card_list(i).card_num is not null
then
dbms_output.put_line (acct_card_list(i).card_num);
blokiraj_karticu (acct_card_list(i).card_num);
else
dbms_output.put_line ('No card for this account');
end if;
end loop;
-- exit buffer fetch when current buffeer is not full. As that means all rows
-- from cursor have been fetched/processed.
exit when acct_card_list.count < k_acct_card_buffer_limit;
end loop;
close c_acct_card;
end blokiraj_proc;
Well this is just another approach. If it's better for you, great. I also want to repeat and expand Ed Stevens warning of running this from a trigger. If either of the tables here is the table on which the trigger fired you will still get a mutating table exception - you cannot just hide it behind a procedure. And even if not its a lot of looping for trigger.

PLSQL IMPLICIT CURSOR No Data Found After CURSOR

I have a Main cursor that is working fine.
declare
v_firm_id number;
amount number;
v_total_sum TABLE_TEMP.TOTAL_SUM%TYPE;
CURSOR MT_CURSOR IS
SELECT firm_id FROM t_firm;
BEGIN
OPEN MT_CURSOR;
LOOP
FETCH MT_CURSOR INTO v_firm_id;
EXIT WHEN MT_CURSOR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(to_char(sysdate, 'mi:ss') ||'--- '|| v_firm_id)
INSERT INTO TABLE_TEMP(TOTAL_SUM) VALUES(v_firm_id)
COMMIT;
END LOOP;
DBMS_LOCK.SLEEP(20);
BEGIN
FOR loop_emp IN
(SELECT TOTAL_SUM INTO v_total_sum FROM TABLE_TEMP)
LOOP
dbms_output.put_line(to_char(sysdate, 'mi:ss') ||'--- '|| v_total_sum || '-TEST--');
END LOOP loop_emp;
END;
end;
Everything Works fine except dbms_output.put_line(v_total_sum || '---');
I do not get any data there. I get the correct number of rows. which it inserted.
The problem is the cursor FOR loop has a redundant into clause which it appears the compiler silently ignores, and so v_total_sum is never used.
Try this:
begin
for r in (
select firm_id from t_firm
)
loop
insert into table_temp (total_sum) values (r.firm_id);
end loop;
dbms_lock.sleep(20);
for r in (
select total_sum from table_temp
)
loop
dbms_output.put_line(r.total_sum || '---');
end loop;
commit;
end;
If this had been a stored procedure rather than an anonymous block and you had PL/SQL compiler warnings enabled with alter session set plsql_warnings = 'ENABLE:ALL'; (or the equivalent preference setting in your IDE) then you would have seen:
PLW-05016: INTO clause should not be specified here
I also moved the commit to the end so you only commit once.
To summarise the comments below, the Cursor FOR loop construction declares, opens, fetches and closes the cursor for you, and is potentially faster because it fetches in batches of 100 (or similar - I haven't tested in recent versions). Simpler code has less chance of bugs and is easier to maintain in the future, for example if you need to add a column to the cursor.
Note the original version had:
for loop_emp in (...)
loop
...
end loop loop_emp;
This is misleading because loop_emp is the name of the record, not the cursor or the loop. The compiler is ignoring the text after end loop although really it should at least warn you. If you wanted to name the loop, you would use a label like <<LOOP_EMP>> above it. (I always name my loop records r, similar to the i you often see used in numeric loops.)

In Oracle, how to catch exception when table used for the loop doesn't exists

I have this code:
BEGIN
FOR
U1 IN (SELECT * FROM SOME_USER.SOME_TABLE)
LOOP
BEGIN
-- do something;
END;
END LOOP;
END;
My problem is that sometimes SOME_USER.SOMETABLE do not exists but I want the rest of the script to be run. I know that checking if the table exists before running the code (in a IF ... THEN block) will not work because SELECT * FROM SOME_USER.SOME_TABLE is evaluated at compile time.
So another avenue is to run the SELECT with EXECUTE IMMEDIATE. This way it will be evaluated at run time and I would be able to catch the exception. Unfortunately I can't find a way to use EXECUTE IMMEDIATE with my U1 IN loop. How I should achieve this?
I'm on Oracle 11g and the SQL script is run from a batch script on Windows.
You can use the 'OPEN FOR' syntax:
DECLARE
CUR SYS_REFCURSOR;
<variables or record type> -- declare as appropriate
BEGIN
OPEN CUR FOR 'SELECT * FROM SOME_USER.SOME_TABLE';
LOOP
FETCH CUR INTO <variables or record type>;
EXIT WHEN CUR%NOTFOUND;
-- do something with variables or record
END LOOP;
CLOSE CUR;
END;
/
You need to fetch each row into variables or a record type, you can't use %ROWTYPE as the table still won't exist; and you can change to do bulk fetches if that's appropriate for your data volumes.
If you run that you'll still get ORA-00942, but if this is in a stored program you won't get it until run time, and you can now add an IF block to check for the table's existence before the OPEN.
Having a data model where objects may or may not exist at run-time seems rather fishy though...
Proposed solution with cursor is fine, I would add an exception handling for this particular exception : Table or view does not exist ORA-00942.
DECLARE
e_missing_t EXCEPTION;
pragma exception_init (e_missing_t,-942);
something number; --some variable you need to fetch to
CUR SYS_REFCURSOR;
BEGIN
OPEN CUR FOR 'SELECT * FROM SOME_USER.SOME_TABLE';
LOOP
FETCH CUR INTO something;
EXIT WHEN CUR%NOTFOUND;
-- do something with variables or record
END LOOP;
CLOSE CUR;
EXCEPTION
WHEN e_missing_t THEN
dbms_output.put_line('some_table is missing');
END;
/
You could possibly use a workaround -
Create a nested table type and store the results of the SELECT in it. Use that type to loop through values.
So,
SELECT data_obj(COL1, COL2) bulk collect into data_tbl_typ from data_table;
This part can go in the dynamic sql. (Remember to use bind variables)
And then just loop through this nested table type in your procedure.
Use the DBMS_SQL package to run the query.
Follow the examples in this Oracle documentation:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htm#sthref6147
pl/sql has the exception clause for that. OTHERS catches pretty much everything. You can deal with the exception in the function, or print a message and pass it back to the main. Break your function up into smaller functions and have each one catch it's own exception.
BEGIN
FOR
U1 IN (SELECT * FROM SOME_USER.SOME_TABLE)
LOOP
BEGIN
-- do something;
END;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('Oh well. The table isn't there.');
--RAISE;
END;

Resources