Triggers involving two tables - oracle

I have two relations(tables) namely and 'department' and 'employee'
create table department(depno numeric(5),depname varchar2(10),deplocation varchar2(7));
create table employee(empno numeric(5),empname varchar2(20),designation varchar2(20),depno numeric(5));
I'm trying to write a database trigger before deleting each row on table department deletes
corresponding department employees from employee table..
My code looks like
create or replace trigger tri before delete on department for each row
begin
delete from employee where depno=:old.depno;
end;
I inserted few data into my relation and tried to delete one of them having the depno as 2
'''delete from department where depno=2;'''
But it shows an error like this
ORA-04098: trigger 'SYSTEM.T1' is invalid and failed re-validation
Can you please help me with this or provide me an alternative solution to this problem?

Related

Trigger of two tables in an audit

Good morning, I am trying to audit two tables, and I have investigated that it cannot be done, therefore I explain what I wanted to do:
I have two tables (Participante, Actividad) which are joined by a third table (Part_Actividad)
I want to audit the Participants table, but in the same way I need the Id of the activity to know in which activity the data of a participant is changed.
Ideas I had
Create a trigger like the following
CREATE OR REPLACE TRIGGER Tri_Auditoria
AFTER INSERT ON Participante
FOR EACH ROW
DECLARE
v_Participante_ID Participante.Participante_ID%TYPE;
v_Actividad_ID Part_Actividad.Actividad_ID%TYPE;
BEGIN
SELECT participante_ID INTO v_Participante_ID
FROM Participante;
SELECT Actividad_ID INTO v_Actividad_ID
FROM Part_Actividad
WHERE PartAct_ID = v_Participante_ID;
INSERT INTO Auditoria(Auditoria_ID, Actividad_ID, Participante_ID, TipPart_ID_Ant, Part_P_Nombre_Ant, Part_P_Apell_Ant, Part_Cedula_Ant, Part_Genero_ant, Part_FNaci_Ant, Aud_Operacion, Usuario_Modificador, Fecha_Modificacion)
VALUES (sec_Auditoria.nextval, v_Actividad_ID, :new.Participante_ID,:new.TipPart_ID,:new.Part_P_Nom,:new.Part_P_Apell,:new.Part_Cedula,:new.Part_Genero,:new.Part_FNaci,'I',USER,sysdate);
END Tri_Auditoria;
/
Result:
When I insert data into the participante table, it is not inserted and it sends a trigger error.
Mutating table error, isn't it? That's because you're selecting from the same table which caused trigger to fire, and that's not allowed.
Anyway, you shouldn't do that because you have that value in disposal - just reference it using the :new pseudorecord, such as
CREATE OR REPLACE TRIGGER Tri_Auditoria
AFTER INSERT ON Participante
FOR EACH ROW
DECLARE
v_Actividad_ID Part_Actividad.Actividad_ID%TYPE;
BEGIN
SELECT Actividad_ID
INTO v_Actividad_ID
FROM Part_Actividad
WHERE PartAct_ID = :new.participante_ID; --> this
INSERT INTO Auditoria
(Auditoria_ID, Actividad_ID, Participante_ID,
TipPart_ID_Ant, Part_P_Nombre_Ant, Part_P_Apell_Ant,
Part_Cedula_Ant, Part_Genero_ant, Part_FNaci_Ant,
Aud_Operacion, Usuario_Modificador, Fecha_Modificacion)
VALUES (sec_Auditoria.nextval, v_Actividad_ID, :new.Participante_ID,
:new.TipPart_ID,:new.Part_P_Nom,:new.Part_P_Apell,
:new.Part_Cedula,:new.Part_Genero,:new.Part_FNaci,
'I',USER,sysdate);
END Tri_Auditoria;
/

Oracle DB. Insert Trigger with Merge statament inside. Table is mutating

I have two back-end systems (the old one and the new one) that shares an Oracle DB.
In the older system, to save customers data, there are two tables
customers_A
ID NAME ETC
1 PETE ....
customers_B
ID NAME ETC
1 JOSH ...
2 ROSS ...
In the new system I've created a new table called All_Costumer, to join those tables.
This new table contains customer ID's of type A and B respectively.
All_Customers
ID ID_CUSTOMER_A ID_CUSTOMER_B
A19E----D2B0 1 null
A19E----D2B1 null 1
A19E----D2B2 null 2
So, when the new system creates a new customer of type A, data are inserted on customer_A and All_Customers tables, with customer of type B as well.
Currently, the old system is working too, and when a new customer of type A is created, data is inserted only on customer_A table, but I need that data in All_Customers too.
To solve this, I've created a TRIGGER with a MERGE INTO statement inside, to insert a row in All_Customers if doesn't exist on this table (when a new customer of type A are created by the older system)
CREATE OR REPLACE TRIGGER customers_trg
AFTER INSERT
ON customer_A
FOR EACH ROW
DECLARE
variables that doesn't matters
BEGIN
MERGE INTO all_customers
USING (SELECT :new.id id FROM customer_A where id = :new.id) customer
ON (all_customers.id_customer_a = customer.id)
WHEN NOT MATCHED THEN
INSERT (id, id_customer_a)
VALUES (SYS_GUID(), :new.id, null);
COMMIT;
END;
But when I try to create a new customer from the older system, I get this error:
ORA-04091: table **customer_A** is mutating, trigger/function may not see it
Any idea to solve this?
I've tried adding PRAGMA AUTONOMOUS_TRANSACTION; on DECLARE section, but didn't work.
Note: I can't modify the old system
The immediate issue is that you're querying table_a in a trigger against that table; but you don't need to. Your merge query
SELECT :new.id id FROM customer_A where id = :new.id
can simply do
SELECT :new.id id FROM dual
i.e. the clause becomes:
...
USING (SELECT :new.id id FROM dual) customer
ON (all_customers.id_customer_a = customer.id)
...
You also can't commit in a trigger - unless it's autonomous, which this shouldn't be. You said you'd tried that, but it breaks if the insert is rolled back, since the merged row will still exist. So hopefully that commit is just a hang-over from trying and rejecting that approach.
But it works in this db<>fiddle, anyway.
If you weren't adding the GUID you could get the same effect with a view:
create or replace view all_customers (id_customer_a, id_customer_b) as
select id, null from customers_a
union all
select null, id from customers_b;
db<>fiddle

Triggering after an update or delete on a FK constraint between 2 tables in oracle

I have the following tables:
create table emp_test_lucian as select employee_id,last_name,first_name,department_id from employees;
ALTER TABLE emp_test_lucian
ADD PRIMARY KEY (employee_id);
create table dept_test_lucian as select department_id,department_name from departments_copy;
ALTER TABLE dept_test_lucian
ADD PRIMARY KEY (department_id);
On this tables I want to perform different operations for example: If a department gets deleted (from dept_test_lucian) I will delete all the rows in emp_test_lucian that have that department id with a trigger. This works fine when no fk between the 2 is declared with the following code :
CREATE OR REPLACE TRIGGER triger5
BEFORE UPDATE or DELETE on dept_test_lucian
FOR EACH ROW
BEGIN
IF DELETING then
delete
from emp_test_lucian
where department_id = :OLD.department_id;
else if UPDATING('department_id') then
UPDATE emp_test_lucian
set department_id = :NEW.department_id
where department_id = :OLD.department_id;
END IF;
END IF;
END;
/
What can I add to the code above to work even if I have a fk between the 2 tables like so:
ALTER TABLE emp_test_lucian
ADD CONSTRAINT fk_dep_id FOREIGN KEY(department_id) REFERENCES dept_test_lucian(department_id);
the current code returns :
Error report:
ORA-00001: unique constraint (C##LABORATOR.SYS_C009994) violated
ORA-06512: at line 2
00001. 00000 - "unique constraint (%s.%s) violated"
*Cause: An UPDATE or INSERT statement attempted to insert a duplicate key.
For Trusted Oracle configured in DBMS MAC mode, you may see
this message if a duplicate entry exists at a different level.
*Action: Either remove the unique restriction or do not insert the key.
You need to make clear what table is the 'parent' and what table is the 'child'.
In your example:
- Parent: dept_test_lucian
- Child: emp_test_lucian
Lets call 'dept_test_lucian': TableA
Lets call 'emp_test_lucian': TableB
I come to this conclusion since there is a CONSTRAINT on TableB.department_id" that can only have a value that exists
in "TableA.department_id"
The error message tells you that there is a 'primary key being vialated'.
Primary keys on you tables are:
- "TableA.employee_id"
- "TableB.department_id"
Apparently you are trying to insert a value in one of these columns where that value already exists in.
If '1' is already existing in "TableA.employee_id" you would get such an error.
What I also see in your trigger is:
You have a BEFORE UPDATE Trigger.
So the Trigger looks if there is an UPDATE comming on "TableA" (Parent).
Then you try to UPDATE "TableB" (child) first.
This could be tricky, since "TableB.department_id" can only have values that exist in "TableA.department_id".
If the new UPDATE value doesn't exist in "TableA.department_id", you can not UPDATE that value in "TableB.department_id"

oracle trigger on delete

Okay so I have been stuck on this for about 2 hours now and I still couldn't find a solution.
I have 2 database instances.
Site 1 has lets say,
Table A
id
attrib1
foreignKey - (primary key of table B)
Site 2 has,
Table B
id
attrib1
I want to create a trigger on delete of a record of Table A. Which basically checks if Site 2 Table B has a reference to that particular record. If it does have that record, I want to prevent the deletion from happening. So far I have come up with this,
CREATE OR REPLACE TRIGGER CHECK_DEALERSHIP_USAGE
BEFORE DELETE on TBL_CARDEALERSHIP
FOR each ROW
declare
rowcnt number;
begin
SELECT COUNT(DEALERSHIP_ID) INTO rowcnt
from TBL_SALESPEOPLE#SITE1
where DEALERSHIP_ID = :NEW.DEALERSHIP_ID;
if (rowcnt>0) THEN
Raise_Application_Error (-20100, 'This dealership is used in the sales people table.');
end if;
end;
Then I do this,
delete from TBL_CARDEALERSHIP
where DEALERSHIP_ID='83';
But it still deletes it, even thought I have a record in the database
As DrabJay put it in the comments, the NEW record in a delete trigger is NULL, since it's after the deletion.
But, frankly, you're going at this wrong. This sort of thing should be done with foreign keys, not in a trigger.
CREATE TABLE TBL_CARDEALERSHIP (
... columns ...
CONSTRAINT fk_salesppl FOREIGN KEY (dealership_id)
REFERENCES tbl_salespeople (dealership_id)
);

TRIGGER Oracle to prevent updating or inserting

I am having problems with this code below, which is a trigger used in Oracle SQL:
CREATE OR REPLACE TRIGGER TRG_TUTOR_BLOCK
BEFORE INSERT OR UPDATE ON tutors
FOR EACH ROW
DECLARE
BEGIN
IF :new.tutorName = :old.tutorName
THEN
RAISE_APPLICATION_ERROR(-20101, 'A tutor with the same name currently exists.');
ROLLBACK;
END IF;
END;
/
This trigger is used to prevent users from entering the same tutor name at different records.
After I insert two records with the same tutorname, the trigger does not block me from inserting it. Is there anyone can tell me what are the problems with this coding? Here are the sample format and insert values:
INSERT INTO tutors VALUES (tutorID, tutorName tutorPhone, tutorAddress, tutorRoom, loginID);
INSERT INTO tutors VALUES ('13SAS01273', 'Tian Wei Hao', '019-8611123','No91, Jalan Wangsa Mega 2, 53100 KL', 'A302', 'TianWH');
Trigger in Kamil's example will throw ORA-04091, you can see this with your own eyes here. ROLLBACK in a trigger is unnecessary, it runs implicitly when a trigger makes a statement to fail.
You can prohibit any DML on table by altering it with read only clause:
alter table tutors read only;
At last, integrity should be declarated with integrity constraints and not with triggers.
Good luck!
You don't need a trigger for this in Oracle.
You can do it with an "unique index" on the tutorName column (see http://docs.oracle.com/cd/B28359_01/server.111/b28310/indexes003.htm#i1106547).
Note: about your trigger, it fails on checking for another record with the same tutorName because it's not scanning the tutors table for another record with the same tutorName, it's just comparing the tutorName values of the row you are creating (in this case, old.tutorName is just NULL, because the row doesn't exist yet).
Check the case in yours trigger body
IF :new.tutorName = :old.tutorName
It returns true only if 'tutorName' value is the same in new and old record. When you'll trying to updat some value you'll get
IF 'someTutorName' = 'someTutorName'
which will return TRUE.
Inserting row cannot fire this rule because you're trying to compare something like that:
'someTutorName' = NULL
This case always returns FALSE.
Try to use something like that
CREATE OR REPLACE TRIGGER TRG_TUTOR_BLOCK
BEFORE INSERT OR UPDATE ON tutors
FOR EACH ROW
DECLARE
rowsCount INTEGER;
BEGIN
SELECT COUNT(*) FROM tutors WHERE tutorName is :new.tutorName INTO rowsCount;
IF rowsCount > 0
THEN
RAISE_APPLICATION_ERROR(-20101, 'A tutor with the same name currently exists.');
ROLLBACK;
END IF;
END;
/
But the best solution is the one mentioned by friol - use unique index by executing SQL like this
ALTER TABLE tutors
ADD CONSTRAINT UNIQUE_TUTOR_NAME UNIQUE (tutorName);
If you wanna completely ignore recording a row to a table you can follow these steps
rename table to something else and create a view with the same name and create an instead of trigger.
create table usermessages (id number(10) not null)
GO
alter table usermessages rename to xusermessages
GO
create or replace view usermessages as (select * from xusermessages)
GO
create or replace trigger usermessages_instead_of_trg
instead of insert or update on usermessages
for each row
begin
Null ;
end ;
GO
insert into usermessages(123)
Live test available here below
http://sqlfiddle.com/#!4/ad6bc/2

Resources