ORACLE PL/SQL Programming problems with trigger - oracle

Hi I got a problem running a trigger created using ORACLE PL/SQL programming. Basically, whenever there is an insertion with a location that does not exist in the database, I have to insert a new tuple into FEATURES table. Here's the Trigger -
CREATE OR REPLACE TRIGGER sightTrigger
AFTER INSERT
ON SIGHTINGS
FOR EACH ROW
DECLARE
x INTEGER;
BEGIN
SELECT COUNT(*)INTO x
FROM FEATURES
WHERE FEATURES.location = :NEW.location;
IF(x=0) THEN
RAISE APPLICATION ERROR(-20001, 'Warning: Insert into the SIGHTINGS ...');
INSERT INTO FEATUERS(LOCATION,CLASS,LATITUDE<LONGITUDE,MAP,ELEV)
VALUES(:NEW.location, 'UNKNOWN', null, null, null, null);
END IF;
END sightTrigger;
It compiled fine but I ran a simple query to test it -
INSERT INTO SIGHTINGS VALUES ('Douglas dustymaiden', 'Person A', 'Piute', TO_DATE('17­Feb­07', 'DD­MON­YY'));
And it gave me an error called -
"ORA-20001: Warning: Insert into the SIGHTINGS..." (What I wanted it)
"ORA-06512: at line 7"
"ORA-04088: error during execution of trigger"
Then the insertion into the FEATURES table didn't occur when I tested it. Please help.

As per documentation on raise_application_error:
When called, raise_application_error ends the subprogram and returns a user-defined error number and message to the application. The error number and message can be trapped like any Oracle error.
So, first do the other insert, then raise the error. However, if you raise an unhandled error in a trigger, that will roll back the entire transaction and any data modifications with it and the trigger will error. You may want to consider returning a message to the user in a different way.

Related

Can a trigger in Oracle saves data to log table and raises an exception as well?

guys:
I wonder if there is a way to write a trigger in Oracle to do both things: saving data to a log table and raising a user defined exception as well?
I am trying to figure out a strange error on my team's database, which causes data inconsistency per business logic. Multiple team's application can access this database. So I wrote a trigger to monitor certain column in a table which causes the problem. I want to save data such as user ID, saving time etc. to a log table if value is incorrect, but I also want to raise exception to attract attention. However, whenever my trigger raises the user defined exception, saving data to log table is not finished. Can anyone give a suggestion about it? Thank you in advance.
You can write a logging function that uses an autonomous transaction
create or replace procedure log_autonomous( p_log_message in varchar2,
p_other_parameters... )
as
pragma autonomous_transaction;
begin
insert into log_table ...
commit;
end;
and then call that logging function from your trigger
create or replace trigger my_trigger
before insert or update on some_table
for each row
declare
begin
if( some_bad_thing )
then
log_autonomous( 'Some message', ... );
raise_application_error( -20001, 'Some error' );
end if;
end;
The log_table message will be preserved because it was inserted in a separate (autonomous) transaction. The triggering transaction will be rolled back because the trigger raises an exception.

Re-run PL/SQL script command

I have a simple insert script that I want to expand upon.
DECLARE
i varchar2(3000) := dbms_random.string('A',8);
BEGIN
INSERT INTO BUYERS
(USER_ID,BUYER_CD,BUYER_ENG_NM,REG_DT)
VALUES
(i,'tes','test','test');
EXCEPTION WHEN OTHER
THEN
(this is where I need help)
end;
We have dynamic replication going on between two DB's. However, for some odd reason we have to run a script twice for the changes to commit to both DB's for that reason I am creating a script that will attempt to do a insert amongst all tables. As of now I'm only working on one table. Within the exception handler how do I make the script run again when the initial insert fails? Any help is appreciated.
If a problem happens with the insert then the best approach is to find out what the error is and raise the error. This is best accomplished by an autonomous logging procedure that will record the what, where, when and then RAISE the error again so processing stops. You do not want to take a chance of inserting records once, twice or not at all which could happen if the errors are not raised again.
The LOG_ERROR procedure below can be created from the answers to your previous questions about error handling.
DECLARE
i varchar2(3000) := dbms_random.string('A',8);
BEGIN
INSERT INTO BUYERS
(USER_ID,BUYER_CD,BUYER_ENG_NM,REG_DT)
VALUES
(i,'tes','test','test');
EXCEPTION WHEN OTHER
THEN
--by the time you got here there is no point in trying to insert again
LOG_ERROR(SQLERRM, LOCATION);
RAISE;
end;

PLSQL Trigger: before insert

Am trying to write a trigger which has to restrict for zero salary before insertion.
I have written but its not working. I am getting an error as trigger is invalid and failed re validation.
Code:
create or replace trigger emp_sal
before insert or update on employee
referencing new as new old as old
for each row
begin
if :new.salary<=0 then
raise_application_error (-20999,’salary is zero’);
end if;
end;
/
Please let me know where the problem is. Thank you
Your code is okay. The only problem I can imagine is that you were using the wrong character for the single quotes in ’salary is zero’.
Run show errors right after you created the trigger (assuming you're uning sqlplus)
But I agree with zerkms. This is a case for a check constraint.

create trigger on INSERT IN table for specific values

I am trying to crate a trigger in oracle 12c that will execute procedure if inserted values contain specific values.
What I am trying to do is after certain tables (EVENTS, MARKS, STAGE) are all refreshed, only then I want the trigger to run REFRESH_MVS(); procedure.
And they are stored in COUNTS table after they are refreshed. So I am checking if new INSERT in COUNTS has keyword: EVENTS, MARKS, STAGE.
Is this the way to do it?
CREATE or replace TRIGGER MV_REFRESH
AFTER INSERT ON COUNTS
FOR EACH ROW
DECLARE
MODEL_NAME varchar2(20);
BEGIN
select MODEL INTO MODEL_NAME from COUNTS;
IF(MODEL_NAME = 'EVENTS' AND MODEL_NAME = 'MARKS' AND MODEL_NAME = 'STAGE')
THEN
REFRESH_MVS();
END IF;
END;
After compiling it successfully if I run INSERT:
INSERT INTO COUNTS
values ('EVENTS', '11658495', '0.11', '17-MAR-14', '17-MAR-14');
It throws error:
Error starting at line 3 in command:
INSERT INTO COUNTS
values ('EVENTS', '11658495', '0.11', '17-MAR-17', '17-MAR-17')
Error report:
SQL Error: ORA-04091: table COUNTS is mutating, trigger/function may not see it
ORA-06512: at "MV_REFRESH", line 5
ORA-04088: error during execution of trigger 'MV_REFRESH'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
It does not appear that what you are trying to do is sensible. If you can explain the business problem you are trying to solve, we can probably assist you in coming up with a more appropriate technical implementation.
In general, in a row-level trigger on a table, you cannot query the table in question. It does not appear that you have any need to query the table in this trigger, though. My guess is that you just need to use the :new.model_name. But if that's the case, the logic doesn't make sense-- it is, of course, impossible for a single attribute to have three different values at the same time.
IF(:NEW.MODEL_NAME = 'EVENTS' AND
:NEW.MODEL_NAME = 'MARKS' AND
:NEW.MODEL_NAME = 'STAGE')
THEN
REFRESH_MVS();
END IF;
Perhaps you meant OR rather than AND
IF(:NEW.MODEL_NAME = 'EVENTS' OR
:NEW.MODEL_NAME = 'MARKS' OR
:NEW.MODEL_NAME = 'STAGE')
THEN
REFRESH_MVS();
END IF;
which could be simplified
IF(:NEW.MODEL_NAME IN( 'EVENTS', 'MARKS', 'STAGE') )
THEN
REFRESH_MVS();
END IF;
Now, you'll also get the mutating table exception if the refresh_mvs tries to query the counts table. If the current implementation tries to query counts, you would need to change the procedure to accept as parameters whatever data it requires from the current row that is being inserted.
If refresh_mvs is actually refreshing materialized views, that implies that it is at least doing implicit commits. That would create further issues because you cannot commit in a trigger (unless the trigger is defined as an autonomous transaction which would not be appropriate here).

Oracle PL/SQL: Calling a procedure from a trigger

I get this error when ever I try to fire a trigger after insert on passengers table. this trigger is supposed to call a procedure that takes two parameters of the newly inserted values and based on that it updates another table which is the booking table. however, i am getting this error:
ORA-04091: table AIRLINESYSTEM.PASSENGER is mutating, trigger/function may not see it
ORA-06512: at "AIRLINESYSTEM.CALCULATE_FLIGHT_PRICE", line 11 ORA-06512: at
"AIRLINESYSTEM.CALCULATE_FLIGHT_PRICE", line 15 ORA-06512: at
"AIRLINESYSTEM.CALCULATE_FLIGHT_PRICE_T1", line 3 ORA-04088: error during execution of
trigger 'AIRLINESYSTEM.CALCULATE_FLIGHT_PRICE_T1' (Row 3)
I complied and tested the procedure in the SQL command line and it works fine. The problem seems to be with the trigger. This is the trigger code:
create or replace trigger "CALCULATE_FLIGHT_PRICE_T1"
AFTER
insert on "PASSENGER"
for each row
begin
CALCULATE_FLIGHT_PRICE(:NEW.BOOKING_ID);
end;​​​​​
Why is the trigger isn't calling the procedure?
You are using database triggers in a way they are not supposed to be used. The database trigger tries to read the table it is currently modifying. If Oracle would allow you to do so, you'd be performing dirty reads.
Fortunately, Oracle warns you for your behaviour, and you can modify your design.
The best solution would be to create an API. A procedure, preferably in a package, that allows you to insert passengers in exactly the way you would like it. In pseudo-PL/SQL-code:
procedure insert_passenger
( p_passenger_nr in number
, p_passenger_name in varchar2
, ...
, p_booking_id in number
, p_dob in number
)
is
begin
insert into passenger (...)
values
( p_passenger_nr
, p_passenger_name
, ...
, p_booking_id
, p_dob
);
calculate_flight_price
( p_booking_id
, p_dob
);
end insert_passenger;
/
Instead of your insert statement, you would now call this procedure. And your mutating table problem will disappear.
If you insist on using a database trigger, then you would need to avoid the select statement in cursor c_passengers. This doesn't make any sense: you have just inserted a row into table passengers and know all the column values. Then you call calculate_flight_price to retrieve the column DOB, which you already know.
Just add a parameter P_DOB to your calculate_flight_price procedure and call it with :new.dob, like this:
create or replace trigger calculate_flight_price_t1
after insert on passenger
for each row
begin
calculate_flight_price
( :new.booking_id
, :new.dob
);
end;
Oh my goodness... You are trying a Dirty Read in the cursor. This is a bad design.
If you allow a dirty read, it return the wrong answer, but also it returns an answer that never existed in the table. In a multiuser database, a dirty read can be a dangerous feature.
The point here is that dirty read is not a feature; rather, it's a liability. In Oracle Database, it's just not needed. You get all of the advantages of a dirty read—no blocking—without any of the incorrect results.
Read more on "READ UNCOMMITTED isolation level" which allows dirty reads. It provides a standards-based definition that allows for nonblocking reads.
Other way round
You are misusing the trigger. I mean wrong trigger used.
you insert / update a row in table A and a trigger on table A (for each row) executes a query on table A (through a procedure)??!!!
Oracle throws an ORA-04091 which is an expected and normal behavior, Oracle wants to protect you from yourself since it guarantees that each statement is atomic (i.e will either fail or succeed completely) and also that each statement sees a consistent view of the data
You would expect the query (2) not to see the row inserted on (1). This would be in contradiction
Solution: -- use before instead of after
CREATE OR REPLACE TRIGGER SOMENAME
BEFORE INSERT OR UPDATE ON SOMETABLE

Resources