Oracle Trigger only activates the first IF statement - oracle

I have a trigger that has multiple IF statements (not elseif). I did bring the code back down to having twice the same IF statement but only the first IF statement is run.
Is this standard Oracle trigger behavior? one of the if statements is pretty much hard-coded while the other uses a function so there is only a select few cases where both IFs are to be run.
Is there a different way to approach an issue like this? or would the issue be in the trigger code and should i post it here?
the code below has this behavior on our server but is dummied down from the original.
create or replace TRIGGER V_INV_TRANS_BIZTALK
AFTER INSERT ON INVENTORY_TRANSACTION
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
l_type pre_advice_header.user_def_type_4%type;
l_status order_header.status%TYPE;
l_from_loc_zone location.zone_1%TYPE;
l_to_loc_zone location.zone_1%TYPE;
l_patype pre_advice_header.pre_advice_type%type;
l_retour pre_advice_header.user_def_type_6%TYPE;
l_client pre_advice_header.client_id%TYPE;
BEGIN
l_client := :new.client_id;
--Client_id = SD
IF l_client = 'SD'
THEN
CASE
--InBound + Return : Pre_Advice_header
WHEN (:new.code = 'PreAdv Status' and :new.notes in ('In Progress --> Complete')) THEN
select PRE_ADVICE_TYPE
into l_patype
from pre_advice_header
where pre_advice_id = :new.reference_id
and client_id = :new.client_id;
END CASE;
END IF;
--TRANSPARIX
IF l_client = 'SD'
--(beldba.is_transparix_client(p_client_id => :new.client_id) = '1' )
THEN
CASE
--Order is shipped
WHEN (:new.notes like ('%--> Shipped')) THEN
--TRANSEXT
INSERT INTO beldba.biztalk_trigger
(
event_id,
status,
system_id,
client_id,
reference_id,
receiver_id,
user_def_type_1
)
VALUES
(
'TRANSExt',
'Pending',
'DCS',
:new.client_id,
:new.reference_id,
'TRANSPARIX',
''
);
END CASE;
END IF;
COMMIT;
EXCEPTION when others then
NULL;
END;

I think there are two problems here:
If there are no matching cases in the CASE statement, and there is no ELSE clause, the CASE statement will raise an ORA-06592 error 'CASE not found when executing CASE statement'. If you don't want to do anything when there is no matching case, add the following section to your CASE statement:
ELSE
NULL;
You end your trigger with
EXCEPTION when others then
NULL;
This swallows all exceptions, including the one that Oracle was raising to tell you that it couldn't find a CASE to execute. Of course, as this is at the end of your trigger, once your trigger raises an exception no further trigger code gets executed.
EXCEPTION WHEN OTHERS THEN NULL is, quite frankly, a cardinal sin in Oracle. I cannot recommend strongly enough deleting this section of your trigger.

Removing the case statements and replacing with IF statements resolved the issue.

Related

What will be the ORA code if the Stored Procedure executes nothing?

I have a Stored Procedures which I am calling from Java code. There is an IF block and that IF condition is false the procedure executes nothing. So what will be the ORA code in this case?
The ORA code will be ORA-00000: normal successful completion because no errors occurred.
If you want a different outcome you need to code something specific. What you do depends on the business rules you're enforcing. Perhaps you need to raise an exception? This example tests whether a parameter is populated and hurls an exception if it isn't:
if p_str is null then
raise_application_error(-20000, 'parameter P_STR must be populated');
else
....
end if;
In this scenario the ORA code will be ORA-20000. Oracle reserves error numbers -20999 to -20000 for our own use.
"what if update is running with no change in table"
Same thing. Anything which does not hurl an exception is a successful completion. In this case we can test whether an update changed anything with the sql%rowcount value:
update your_table
set whatever = p_str
where id = p_id;
if sql%rowcount = 0 then
raise_application_error(-20001, 'No rows in YOUR_TABLE match ID = '||p_id);
end if;
The value returned by a call to SQLCODE (the "ORA code" you mention) is always going to be zero unless the function is called from within an exception handler.

Trigger that selects from table that is being deleted

This trigger ends up raising an error regardless of the if statement values. I'm basically selecting from the same table the delete is occurring on and it's not liking it.
CREATE OR REPLACE TRIGGER delete_schedules
AFTER DELETE
ON SCHEDULES
FOR EACH ROW
DECLARE
lParentCond schedules.cond_code%type;
lParentActive schedules.active_flag%type;
lError exception;
BEGIN
if :OLD.thread is not null then
select cond_code, active_flag
into lParentCond, lParentActive
from schedules where schedule_seq = :old.thread;
if lParentCond = 'OK' and lParentActive in ('*', 'F') then
raise lError;
end if;
end if;
EXCEPTION
when lError then
raise;
WHEN OTHERS THEN RAISE;
END delete_schedules;
Any ideas of a workaround?
You're probably getting the dreaded 'MUTATING TABLE' error. Oracle doesn't allow us to fetch data from the table on which the trigger is defined in an AFTER trigger - but in this case you don't need to because the 'old' values are already available. Rewrite your trigger as:
CREATE OR REPLACE TRIGGER delete_schedules
AFTER DELETE
ON SCHEDULES
FOR EACH ROW
BEGIN
if :OLD.thread is not null AND
:OLD.COND_CODE = 'OK' and
:OLD.ACTIVE_FLAG in ('*', 'F')
then
RAISE_APPLICATION_ERROR(-20100, 'Invalid combination of COND_CODE and ACTIVE_FLAG');
end if;
END delete_schedules;
This assumes (based on the use of a singleton SELECT in the question) that there's only one row in SCHEDULES for the given THREAD value. If that's not the case there are other work-arounds, including using a COMPOUND TRIGGER.
Best of luck.

How to resolve fetch out of sequence in oracle?

I have a procedure in which I am often getting the following error in oracle 11g:
ORA-01002: fetch out of sequence ORA-06512:
at "LEAVE.GES_ERP_LEV_FFS_INTERFACE_PRC", line 350 ORA-06512: at line 1.
at line 350 I have-
BEGIN
FOR V_INTERFACE_EMP IN CUR_INTERFACE_EMP LOOP (Line 350)
EXIT WHEN CUR_INTERFACE_EMP%NOTFOUND;
V_ERR_FLAG := 'N';
V_LOCAL_EMP := 'Y';
BEGIN
The Cursor CUR_INTERFACE_EMP is declared as below
SELECT GELF.*
FROM GES_ERP_LEV_FFS_INTERFACE_T GELF
WHERE (GELF.BALANCE_FLAG != 'W'
OR GELF.CASE_FLAG = 'S'
OR SELF.BALANCE_FLAG IS NULL)
AND GELF.PROCESS_FLAG = 'N'
AND GELF.DATE_OF_RELEASE <= TRUNC(SYSDATE);
If i update some records of the table with Process_Flag Y,the batch works fine for some time and then again after some days we get this same issue.
Please help,let me know in case data is also needed for the mentioned table.
If i update some records of the table with Process_Flag Y,the batch
works fine for some time and then again after some days we get this
same issue.
You try to fetch from a SELECT FOR UPDATE, however a COMMIT has already been issued before it.
I think you have a COMMIT somewhere INSIDE the LOOP which is causing this issue.
A quote by Tom Kyte here:
for x in ( select rowid rid, t.* from T ) loop
update T set x = x+1 where rowid = x.rid;
commit;
end loop;
That implicit cursor is fetched from "across a commit". It is the
practice of keeping a cursor open after committing. It is a bad
practice and is a common cause of ORA-1555 (the above looping
construct in particular)
Also, you are using a CURSOR FOR LOOP. The CURSOR FOR LOOP will terminate when all of the records in the cursor have been fetched. So, you don't need to EXIT explicitly.
You could simply do it as:
FOR V_INTERFACE_EMP IN CUR_INTERFACE_EMP
LOOP
V_ERR_FLAG := 'N';
V_LOCAL_EMP := 'Y';
...
END LOOP;
I was facing the same issue of fetch out of sequence in oracle plsql.
The problem was below line of code which was used in a procedure that was getting called from inside the loop.
-------------problem code--------
if p_ret is null
then
p_ret := 'SUCCESS';
rollback;
return;
end if;
-------------problem code--------
I should not have used rollback.
So, watch out for rollback and commit inside the loop statement.

SIGNAL SQLSTATE Inside Trigger Failing

I have a Product table with 4 columns. 2 columns are price. If the ListPrice column is updated to below a specified amount (StandardCost * 1.2) then the update should fail and the old ListPrice should remain. I am attempting to use a SIGNAL SQLSTATE error to prevent the update from occurring if the criteria are met.
I've been combing Google and tried various variations in the syntax, but I keep hitting the following error while compiling my trigger - "PLS-00103 - Encountered the symbol 'SQLSTATE' when expecting one of the following: := , ( # %"
Any help is greatly appreciated.
CREATE OR REPLACE TRIGGER Product_Price_Check
BEFORE INSERT OR UPDATE OF ListPrice ON Product
FOR EACH ROW
DECLARE
min_price NUMBER(10, 2);
new_price NUMBER(10, 2);
BEGIN
min_price := (:OLD.StandardCost*1.2);
new_price := (:NEW.ListPrice);
IF (new_price < min_price) THEN
-- Rolls back an explicit or implicit transaction to the beginning of the transaction
dbms_output.put_line('the price can’t be below ' || TO_CHAR(min_price));
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insert/update failed';
END IF;
END;
As mustaccio said, you're mixing MySQL syntax with an Oracle trigger. You want raise_application_error:
BEGIN
IF :NEW.ListPrice < (:OLD.StandardCost*1.2) THEN
raise_application_error(-20001,
'the price can’t be below ' || TO_CHAR(:OLD.StandardCost*1.2));
END IF;
END;
/
This won't roll back the transaction, just the update statement. The caller will receive the exception and decide how to handle it - whether to try again, roll back, or commit any other changes already made.
This assumes the old standard cost cannot be null. You might also want to specify a format model for the to_char().
Also don't rely on dbms_output for informing the caller about anything, as you won't know if the caller is looking at or doing anything with the buffer.

Oracle Exception Handling - Is this correct?

I have the following...
IF CONDITION1 THEN
-- SELECT STATEMENT MIGHT RETURN DATA
IF CONDITION2 THEN
-- SELECT COUNT
IF CONDITION3 THEN
INSERT INTO TABLE
(
---
)
VALUES (
---
);
End IF;
END IF;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN;
END of Trigger
Is this a correct way of handling exception for select statement inside CONDITION1?
PL/SQL has no way to return to the site of the error, so you need to create a block around the portion you want to ignore specific errors:
IF CONDITION1 THEN
BEGIN
-- SELECT STATEMENT MIGHT RETURN DATA
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
IF CONDITION2 THEN
-- SELECT COUNT
IF CONDITION3 THEN
INSERT INTO TABLE
(
---
)
VALUES (
---
);
End IF;
END IF;
END IF;
END TRIGGER_NAME;
An alternative is to use an explicit cursor, which does not return an error when it is empty:
DECLARE
CURSOR cur_sample is select dummy from dual where 1=0;
v_dummy dual.dummy%type;
BEGIN
IF CONDITION1 THEN
open cur_sample;
fetch cur_sample into v_dummy;
close cur_sample;
IF CONDITION2 THEN
-- SELECT COUNT
IF CONDITION3 THEN
INSERT INTO TABLE
(
---
)
VALUES (
---
);
End IF;
END IF;
END IF;
END;
Depends what you mean by "correct". What you have presented is syntactically valid, yes. But you haven't told us what you actually want to happen so we can't tell you whether the code you posted will actually do what you want.
From a business logic standpoint, are you certain that it really is not an error if your SELECT INTO returns 0 rows? If you are catching and swallowing an exception, that means that you know that it's not really an error. If you're coding a SELECT INTO, however, that implies that you expect exactly one row. It's certainly possible that both of these statements are true but it would be more common that it really is an exception and that it shouldn't simply be swallowed and ignored.
In general, I would prefer to put the exception handler as close as possible to the query that might throw the exception as possible. I would find it cleaner to have something like
IF condition1
THEN
BEGIN
<<select statement>>
EXCEPTION
WHEN no_data_found
THEN
<<do something>>
END;
IF condition2
THEN
...
END IF;
END IF;
That way, if you end up with multiple places in your procedure where a NO_DATA_FOUND exception might be thrown, it will be clear which exceptions are expected and which are unexpected.
When you get to the point of having three layers of nested IF statements, I would tend to suspect that you want to refactor the code into multiple procedures to make the code clearer. For example, rather than having a nested PL/SQL block that executes the SELECT statement, catches the exception, and handles the exception, it would probably be clearer to have a separate function that did all that and then call that function from your trigger.

Resources