oracle trigger on delete - oracle

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)
);

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 Trigger: Insert into Transaction Entity New Row Upon Update, Insert of Account Entity

I'm a newbie to SQL and I'm having a real hard time setting up this trigger. It's for a Bank Console JDBC sort of thing.
I have a schema with three entities, USER, ACCOUNT, and TRANSACTION. I want to keep track of all the changes a user makes to one of her accounts by inserting a new row into my transaction entity, which has columns of
id, which I'm handling with a sequence,
user_id (referencing a foreign key stored in the accounts entity),
account_id (referencing the account entity's primary key),
a time stamp, (for which I'm using Oracle's CURRENT_TIMESTAMP function),
and a transaction-type, which is either of 'deposit' or 'withdrawal'.
Here's how my Trigger looks right now.
CREATE OR REPLACE TRIGGER ADD_TX
ON ACCOUNT
AFTER INSERT, UPDATE
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE old_balance number, new_balance number, transaction_type varchar2(100);
BEGIN
transaction_type := CASE WHEN :NEW.balance < :OLD.balance THEN 'WITHDRAWAL' ELSE 'DEPOSIT' END;
INSERT INTO TRANSACTIONS VALUES(TRANSACTION_ID_SEQ.NEXTVAL, :NEW.USER_ID, :NEW.id, CURRENT_TIMESTAMP, :NEW.account_type, transaction_type);
end if;
END;
/
Any guidance would be most appreciated
Something like this might do the job:
CREATE OR REPLACE TRIGGER add_tx AFTER
INSERT OR UPDATE ON account
FOR EACH ROW
BEGIN
INSERT INTO transactions VALUES (
transaction_id_seq.NEXTVAL,
:new.user_id,
:new.id,
current_timestamp,
:new.account_type,
CASE
WHEN :new.balance <:old.balance THEN 'WITHDRAWAL'
ELSE 'DEPOSIT'
END
);
END;
/
Though, I'd suggest you to name ALL columns you're inserting to; the way you wrote it, it is unclear which value goes into which column, and such a code might (and probably will) break, some day.

PL/SQL Stored Procedure to Populate Fact Table

I need to populate this fact table using a PL/SQL stored procedure:
CREATE TABLE SALES_FACTS
(saleDay DATE,
vehicleCode INT,
planID INT,
dealerID INT,
vehiclesSold INT,
grossSalesAmt NUMBER(10),
CONSTRAINT SALE_DAY_FK FOREIGN KEY (saleDay) REFERENCES TIMES(saleDay) ON DELETE CASCADE,
CONSTRAINT VEHICLE_CODE_FK FOREIGN KEY (vehicleCode) REFERENCES VEHICLES(vehicleCode) ON DELETE CASCADE,
CONSTRAINT PLAN_ID_FK FOREIGN KEY (planID) REFERENCES FINANCING_PLANS(planID) ON DELETE CASCADE,
CONSTRAINT DEALER_FK FOREIGN KEY (dealerID) REFERENCES DEALERSHIPS(dealerID) ON DELETE CASCADE,
CONSTRAINT SALES_FACTS_PK PRIMARY KEY (saleDay, vehicleCode, planID, dealerID));
I have been asked to do this by using four nested cursor loops to get every possible combination of the dimension tables' primary keys, along with the total vehicles sold and gross sales amount for each combination.
Also, if the values for vehiclesSold and grossSalesAmount are zero, then a row SHOULD NOT be inserted into the SALES_FACTS table.
Only rows for combinations of the four foreign key columns where there were some vehicles sold should be inserted.
I have created the following code that I hoped would accomplish this:
CURSOR factData IS
SELECT vehicleVin,saleDate,sf.planID,sp.dealerID
COUNT (*) AS vehiclesSold
SUM (s.grossSalePrice) AS grossSalesAmount
FROM SALES s, SALES_FINANCINGS sf, SALESPERSONS sp
WHERE s.saleID = sf.saleID
AND s.salespersonID = sp.salespersonID
GROUP BY vehicleVIN, saleDate, sf.planID, sp.dealerID
HAVING COUNT(*) > 0;
BEGIN
FOR record IN factData
LOOP
INSERT INTO SALES_FACTS (saleDay,vehicleCode,planID,dealerID,vehiclesSold, grossSalesAmount
VALUES (record.saleDate,record.vehicleVin,record.planID,record.dealerID,record.vehiclesSold,record.grossSalesAmount);
END LOOP;
END;
/
However the code executes fine, but I do not get any results when I run a
SELECT COUNT(*) FROM SALES_FACTS;
I have created an SQL Fiddle link here http://sqlfiddle.com/#!4/9708d6/1 since the code for the tables and table population was too much to post on this question. Keep in mind that I only INSERTed about 2-3 rows of data for each table to keep the code somewhat short, however the data that has been inserted should suffice to get this working.
Please let me know where I'm going wrong and what the best way to fix it is! Thanks in advance!
This Ended up doing the trick. Thanks for all of the help to those who commented.
DECLARE
CURSOR sales_data
IS
SELECT vehicleVIN, saleDate, SF.planID, SP.dealerID,
COUNT(*) AS vehiclesSold, SUM(S.grossSalePrice) AS grossSalesAmt
FROM SALES S, SALES_FINANCINGS SF, SALESPERSONS SP, VEHICLES V
WHERE S.saleID = SF.saleID AND S.vehicleVIN = V.vehicleCode AND S.salespersonID = SP.salespersonID
GROUP BY vehicleVIN, saleDate, SF.planID, SP.dealerID
HAVING COUNT(*) > 0;
BEGIN
FOR record IN sales_data
LOOP
INSERT INTO SALES_FACTS (saleDay,vehicleCode,planID,dealerID,vehiclesSold, grossSalesAmt)
VALUES (record.saleDate,record.vehicleVIN,record.planID,record.dealerID,record.vehiclesSold,record.grossSalesAmt);
END LOOP;
END;
/

oracle on delete cascade with condition

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

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