I have two tables that here are involved from two different schemas.
Schema services and table task -- column ID
Schme mona_internal and table officius_unos -- column task
I need trigger when inserting in column task table officius_unos to check does exist inserting value in column id from table task. If exist, to continue inserting, it doesn't exist to raise the error.
Here is the trigger:
CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA
BEFORE INSERT ON OFFICIUS_UNOS
FOR EACH ROW
DECLARE
task_provera number(10);
BEGIN
select id into task_provera from servis.task
where id=:new.task;
if (task_provera is null)
then raise_application_error(-20101, 'No task');
else insert into mona_internal.OFFICIUS_UNOS (task) values (:new.task);
end if;
END;
The trigger is compiled, but the problem appears when trying to insert a new value in column task table officius_unos,
it returns me this message
insert into officius_unos (task) values (291504);
Error report -
ORA-00036: maximum number of recursive SQL levels (50) exceeded
ORA-00036: maximum number of recursive SQL levels (50) exceeded
ORA-06512: at "MONA_INTERNAL.PROBA_PROBA", line 5
ORA-04088: error during execution of trigger 'MONA_INTERNAL.PROBA_PROBA'
ORA-06512: at "MONA_INTERNAL.PROBA_PROBA", line 10
And value 291504 exist in table task in column id.
P.S. Also try to solve this problem with check constraint, but there are forbidden subqueries. The solution that I used to overcome my problem is here
Oracle: Using subquery in a trigger
You don't need to insert in an insert trigger.
If the trigger is successful Oracle will continue with the INSERT on its own.
So the immediate solution is to remove the INSERT from the trigger:
CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA
BEFORE INSERT ON OFFICIUS_UNOS
FOR EACH ROW
DECLARE
task_provera number(10);
BEGIN
select id
into task_provera
from servis.task
where id=:new.task;
if (task_provera is null) then
raise_application_error(-20101, 'No task');
end if;
// nothing do do here
END;
However the above still isn't correct. If the id doesn't exist in servis.tak the SELECT will throw a NO_DATA_FOUND exception.
One solution to that is to use an aggregate function that will always return one row. If no rows match the WHERE criteria, a NULL value is returned:
CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA
BEFORE INSERT ON OFFICIUS_UNOS
FOR EACH ROW
DECLARE
task_provera number(10);
BEGIN
select max(id)
into task_provera
from servis.task
where id=:new.task;
if (task_provera is null) then
raise_application_error(-20101, 'No task');
end if;
// nothing do do here
END;
Or you could explicitely catch the exception:
CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA
BEFORE INSERT ON OFFICIUS_UNOS
FOR EACH ROW
DECLARE
task_provera number(10);
BEGIN
select max(id)
into task_provera
from servis.task
where id=:new.task;
if (task_provera is null) then
raise_application_error(-20101, 'No task');
end if;
EXCEPTION
WHEN NOT_DATA_FOUND THEN
raise_application_error(-20101, 'No task');
END;
But the correct approach is to use a foreign key constraint for something like that.
alter table mona_internal.PROBA_PROBA
add constraint fk_proba_task
foreign key (task)
references servis.task (id);
Then you don't need a trigger at all.
This requires that the user mona_internal is not only granted the SELECT privilege on servis.task, but also the references privilege:
To do that, run the following as the SERVIS user:
grant references on task to mona_internal;
Related
Im currently trying to implement a row trigger that fires when the new Employee number inserted into the table is not continuous.
"Continuous" in a relationship to the Employee number means the first record inserted will have the Employee number 1, the second record will have the employee number 2, and each next position must have a number greater by one that a number of the previous position.
I have successfully created the trigger, however when I inserted a new record that have an Employee number that is not continuous, my trigger is not fired.
Im unsure where I went wrong and hope I can get some explanation and corrections on my code.
CREATE OR REPLACE TRIGGER CONTENUM
AFTER INSERT ON TRKEMPLOYEE
FOR EACH ROW
DECLARE
continuous_value EXCEPTION;
PRAGMA exception_init(continuous_value, -20111);
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
IF (:NEW.E# > :OLD.E# + 1) THEN
RAISE_APPLICATION_ERROR (-20111,'The value of Employee number must be continuous');
END IF;
END CONTENUM;
/
Here is the format of my sample TRKEMPLOYEE table
CREATE TABLE TRKEMPLOYEE(
E# NUMBER(12) NOT NULL,
NAME VARCHAR(50) NOT NULL,
DOB DATE ,
ADDRESS VARCHAR(300) NOT NULL,
HIREDATE DATE NOT NULL,
CONSTRAINT TRKEMPLOYEE_PKEY PRIMARY KEY(E#) );
Here is my insert statement.
Currently in my table TRKEMPLOYEE there is only 15 rows thus with my insert statement, the trigger should fire but it is not happening.
INSERT INTO TRKEMPLOYEE VALUES( 17, 'David', NULL, 'GB',sysdate );
Thank you.
First of all you are checking AFTER INSERT ON TRKEMPLOYEE which will be executed after the row is inserted.
Secondly, you cannot check :OLD.E# since you are not updating and you are not using a old value.
Also you should drop the trigger at all and use SEQUENCES and let Oracle take care of the auto-increment values every time you add a new employee.
If you want to continue with your current logic, fixes that can be applied:
Change AFTER INSERT ON TRKEMPLOYEE to BEFORE INSERT ON TRKEMPLOYEE
Logic should be changed as below:
CREATE OR REPLACE TRIGGER contenum BEFORE
INSERT ON trkemployee
FOR EACH ROW
DECLARE
continuous_value EXCEPTION;
PRAGMA exception_init ( continuous_value, -20111 );
PRAGMA autonomous_transaction;
max_e# INTEGER;
BEGIN
SELECT
nvl(MAX(e#), 0)
INTO max_e#
FROM
trkemployee;
IF ( :new.e# > max_e# + 1 ) THEN
raise_application_error(-20111, 'The value of Employee number must be continuous');
END IF;
END contenum;
/
I do not recommend this solution because it will start to become slower as your table starts to grow.
based on role number I am trying to prevent the insert operation. If role number>=103 it should deny.
CREATE OR REPLACE TRIGGER ins_table
BEFORE INSERT ON Tables
FOR EACH ROW
DECLARE
userole number
BEGIN
SELECT role into userole FROM User_account where user_id = :new.user_id;
IF (userole >= 103)
THEN
RAISE_APPLICATION_ERROR(-20001,'Access denied');
END IF;
END;
The above trigger causes an compilation error
I created this trigger that prevents duplicate acct# to be added. But when I tested the trigger by inserting a duplicate acct#, I got error
UNIQUE CONSTRAINT (JL.PK.ACCOUNT) VIOLATED
Please help.
create or replace trigger update_acct#
before insert or update on ACCOUNT
for each row
declare
v_cta# NUMBER;
begin
select count(A#) into v_cta# from account where A#=:new.A#;
if v_cta#>1 then
raise_application_error (-20105, 'DUPLICATE ACCOUNT NUMBER');
end if;
end;
Your trigger did not throw the error. It didn't even fire.
You did not post the table definition from the error provided but it looks like the column A# in define as the primary key. When you attempted to insert the duplicate row the error was thrown as the PK was validated. You do not have to check primary key for duplicates: Oracle guarantees it is unique.
Further had you trigger fired you would have gotten a "ORA-04091 table Name is mutating ... " error. You cannot reference the triggering table within a row level trigger.
You should if condition:
--old code
if v_cta#>1 then
raise_application_error (-20105, 'DUPLICATE ACCOUNT NUMBER');
end if;
--new code
if v_cta#>0 then
raise_application_error (-20105, 'DUPLICATE ACCOUNT NUMBER');
end if;
When v_cta# equals 1 then your condition will not work
Trying to create a trigger that prevents duplicate acct# and null acct balance. Trigger is able to create but when inserting an duplicate acct# to test the trigger, got error mesg that "trigger is invalid and failed re-validation".
create or replace trigger update_acct#
before insert or update on ACCOUNT
for each row
declare
v_cta# NUMBER;
begin
select count(:new.A#) into v_cta# from account group by A#;
if (v_cta#>1 or :new.bal=null) then
raise_application_error (-20011, 'DUPLICATE ACCOUNT NUMBER OR BAL CANNOT BE NULL');
end if;
end;
You absolutely positively do not want to use a trigger for this. Use a unique constraint to prevent duplicate account numbers.
ALTER TABLE account ADD CONSTRAINT a#_unique UNIQUE (a#);
Make your balance field not accept nulls.
ALTER TABLE account MODIFY bal NOT NULL;
For purely educational purposes here is how you'd implement this with your trigger. Yours was close, except you want to use :new.a# to count rows and not have it expressly in the COUNT() and that you want an AFTER trigger since you want to see if your newly added account is the first duplicate.
create or replace trigger update_acct#
after insert or update on ACCOUNT
for each row
DECLARE
v_cta# INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_cta#
FROM account
WHERE a# = :new.a#;
IF v_cta# > 1 OR :new.bal = NULL THEN
raise_application_error(-20011,
'DUPLICATE ACCOUNT NUMBER OR BAL CANNOT BE NULL');
END IF;
END update_acct#;
/
Here in the below trigger duplicate_info exception is used without raising it inside the begin block, I'm not sure how this works. There is no exception when others also but this duplicate_info exception works. Strange!
CREATE OR REPLACE TRIGGER ORDER_INFO_T
INSTEAD OF INSERT ON order_info
DECLARE
duplicate_info EXCEPTION;
PRAGMA EXCEPTION_INIT (duplicate_info, -00001);
BEGIN
INSERT INTO customers
(customer_id, cust_last_name, cust_first_name)
VALUES (
:new.customer_id,
:new.cust_last_name,
:new.cust_first_name);
INSERT INTO orders (order_id, order_date, customer_id)
VALUES (
:new.order_id,
:new.order_date,
:new.customer_id);
EXCEPTION
WHEN duplicate_info THEN
RAISE_APPLICATION_ERROR (
num=> -20107,
msg=> 'Duplicate customer or order ID');
END order_info_insert;
/
When a primary or unique constraint is violated, Oracle raises the error:
ORA-00001: unique constraint (schemaname.constraintname) violated
The trigger has defined the exception duplicate_info and then associated it with the ORA-00001 error via the pragma:
PRAGMA EXCEPTION_INIT (duplicate_info, -00001);
So this means that when Oracle raises error ORA-00001, the associated user-defined exception duplicate_info gets raised.
It's quite redundant though, because Oracle supplies anexception DUP_VAL_ON_INDEX for ORA-00001 already:
CREATE OR REPLACE TRIGGER ORDER_INFO_T
INSTEAD OF INSERT ON order_info
BEGIN
INSERT INTO customers
(customer_id, cust_last_name, cust_first_name)
VALUES (
:new.customer_id,
:new.cust_last_name,
:new.cust_first_name);
INSERT INTO orders (order_id, order_date, customer_id)
VALUES (
:new.order_id,
:new.order_date,
:new.customer_id);
EXCEPTION
WHEN dup_val_on_index THEN
RAISE_APPLICATION_ERROR (
num=> -20107,
msg=> 'Duplicate customer or order ID');
END order_info_insert;