oracle on delete cascade with condition - oracle

I have a foreign key in the table B to the table A. what I want, is on removing record in A, use cascade delete OR cascade set NULL in the FK field in B depends on the value in some column of that record, that should be removed.
As far as I cant use condition in delete cascade, my ideas was to always use cascade SET NULL and add trigger to table B
AFTER UPDATE..
FOR EACH ROW
BEGIN
IF :new.a = 1 THEN
DELETE FROM B WHERE ID = :new.id
ENDIF;
END;
But I get an error, smthing like "table is mutating, trigger/function may not see it".
So, I've changed my if-else to procedure call
AFTER UPDATE
BEGIN
cleanup_table_b();
END;
create or replace procedure cleanup_table_b
IS begin
DELETE FROM B WHERE a = 1;
end;
But still get an error ORA-04091, ORA-06512, ORA-04088
The business idea of that, is the records in B can still be usefull (not by foreign keys) or not, so I want to perform a cleanup and remove useness records.

The following desicion was implemented. Been used a "on delete" trigger on table A to delete or set null the referencing records in table B. So delete trigger ran a cleanup of B

Related

How to drop constraint in trigger in PL/SQL?

I am writing a database for practical purposes which involves some triggers and constraints for the moment.
One of my triggers must set a column value to 0 before deleting a speciffied row , but the deletion cannot work with the constraint on ... so I decided that BEFORE DELETE I remove the constraint then after I update my column value from the other table I will ADD it again using my 2nd trigger.
However I can get it to work :
ALTER TABLE teams ADD CONSTRAINT teams_to_trainer FOREIGN KEY (coda)
REFERENCES trainer(coda);
--insert values
--...
--Procedure in which I execute the alter table commands
CREATE OR REPLACE PROCEDURE constraint_operation (p_hardcod IN VARCHAR2)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
resource_busy EXCEPTION;
PRAGMA EXCEPTION_INIT(resource_busy,-54);
BEGIN
EXECUTE IMMEDIATE p_hardcod;
EXCEPTION
WHEN resource_busy
THEN
DBMS_LOCK.SLEEP(5);
COMMIT;
END;
/
--My Two Triggers
CREATE OR REPLACE TRIGGER delete_trainer
BEFORE DELETE
ON trainer
FOR EACH ROW
BEGIN
constraint_operation('ALTER TABLE teams DROP CONSTRAINT teams_to_trainer');
UPDATE teams SET coda = 0 WHERE :old.coda = coda;
END;
/
CREATE OR REPLACE TRIGGER delete_trainer_add_constraint
AFTER DELETE
ON trainer
FOR EACH ROW
BEGIN
constraint_operation('ALTER TABLE teams ADD CONSTRAINT teams_to_trainer
FOREIGN KEY (coda)REFERENCES trainer(coda)');
END;
/
And when calling :
DELETE FROM trainer WHERE coda = 14;
I get this :
SQL Error: ORA-02291: integrity constraint (USER.TEAMS_TO_TRAINER)
violated - parent key not found
02291. 00000 - "integrity constraint (%s.%s) violated - parent key not found"
*Cause: A foreign key value has no matching primary key value.
*Action: Delete the foreign key or add a matching primary key.
I can't seem to find the problem , I thought calling the procedure will remove the constraint.
What am I doing wrong ?
Thank you.
You are updating a foreign key column in TEAMS to a value that doesn't exist in the referenced primary key in TRAINER.
If the FK constraint is required, a simple fix would be to add a default/dummy row in TRAINER where the value in CODA is 0.

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

Is there a way similar check constraint to help me to know if there is a duplicate column

table 1
ID - name - main_number - random1 - random2
1* -aaaa-blalablabla*- *** - *
2 -vvvv-blublubluuu*- *** - *
3 -aaaa-blalablabla*- *** - **
ID , name and main number are primary key
My problem that I have noticed coulmn name and main number has duplicate values, i dont want to ADD ANY OTHER DUPLICATE VALUES ( I should keep the old duplicat because in my real table there are a lot of duplicated data and its hard to remove them )
what I want when I TRY ( BEFORE TO COMMIT) to know that this name I am trying to insert is duplicate.
I can do that with in a procedure or triger, but i have heard constraint checking is simpler and easier(if there a simpler way then procedure or triger ill be glad to learn it)
CONSTRAINT check_name
CHECK (name = (A_name))
can the constaraint have more then 1 column in such way?
CONSTRAINT check_name
CHECK (name = (A_name) , main_number=( A_number))
can I a write a constaraint in such way?
CONSTRAINT check_name
CHECK (name = ( select case where there is an column has the same value of column name))
So my question : Is there a way simelar to check constraint to help me to know if there is a duplicate column or I have to use a trigger ?
Since your database is Oracle you could also use NOVALIDATE constraints. Meaning: "doesn't matter how the data is, just validate from now on".
create table tb1
(field1 number);
insert into tb1 values (1);
insert into tb1 values (1);
insert into tb1 values (1);
insert into tb1 values (2);
insert into tb1 values (2);
commit;
-- There should be an non-unique index first
create index idx_t1 on tb1 (field1);
alter table tb1 add constraint pk_t1 primary key(field1) novalidate;
-- If you try to insert another 1 or 2 you would get an error
insert into tb1 values (1);
Yes, you can use constraints on many columns.
But in this case constraint is not applicable, because all table rows must satisfy constraints. Use a trigger.
Constraints cannot contain subqueries.
Alternatively use unique index, that will enforce unique constraint
create unique index index1 on table1
(case when ID <= XXX then null else ID end,
case when ID <= XXX then null else name end);
Replace 'XXX' with your current max(ID).
I assume that you want to prevent duplicate records as defined by the combination of name and main_number.
Then the way to go is to cleanup your database, and create a unique index:
create unique index <index_name> on <table> (name, main_number)
This both checks, and speed's it up.
In theory, if you really wanted to keep the old duplicate records, you could get along by using a trigger, but then you will have a hard time trying to get sense out of this data.
Update
If you used the trigger, you would end up with two partitions of data in one table - one is checked, the other is not. So all of your queries must pay attention to it. You just delay your problem.
So either clean it up (by deleting or merging) or move the old data in a separate table.
You can use SQL select ... group by to find your duplicates, so you can delete/move them in one turn.

Resources