PL/SQL Oracle Mutant table-how to encounter this issue plz? - sql

th request is to assure that in the city we have only one branch,
Banch (idBranch(PK), city) for update and insert
the idea I got is to create a temporary table having all the data of the first one so i can escape the ora errors this is the code I wrote, i'm facing the error PL/SQL: ORA-00903 and I'm not so sure if one trigger can do he job in this case since it should be working for inserting and updating. I need your advice please, thank you.
` CREATE TABLE TEMP_BRANCH AS SELECT NumBranch, CITY FROM BRANCH;
CREATE OR REPLACE TRIGGER checkUniqueBranchPerCity
BEFORE INSERT OR UPDATE ON BRANCH
REFERENCING
NEW AS nextLine
FOR EACH ROW
BEGIN
LOCK TABLE TEMP_BRANCH IN ROW SHARE MODE;
FOR line IN (SELECT * FROM TEMP_BRANCH) LOOP
IF :nextLine.City = line.City THEN
RAISE_APPLICATION_ERROR;
END IF;
END LOOP;
DELETE * FROM TEMP_BRANCH;
END;
/ `

ORA-00903 is "Invalid table name". I suspect it's caused because you put a colon in front of nextLine, i.e. you wrote
IF :nextLine.City = line.City THEN
when it should be
IF nextLine.City = line.City THEN
But I suggest getting rid of the REFERENCING clause and just using :NEW and :OLD in triggers as it reduces confusion. So use
CREATE TABLE TEMP_BRANCH AS SELECT NumBranch, CITY FROM BRANCH;
CREATE OR REPLACE TRIGGER checkUniqueBranchPerCity
BEFORE INSERT OR UPDATE ON BRANCH
FOR EACH ROW
BEGIN
LOCK TABLE TEMP_BRANCH IN ROW SHARE MODE;
FOR line IN (SELECT * FROM TEMP_BRANCH) LOOP
IF :NEW.City = line.City THEN
RAISE_APPLICATION_ERROR;
END IF;
END LOOP;
END;
/
I also suggest you get rid of the DELETE statement in the trigger as it will delete the contents of TEMP_BRANCH the first time the trigger fires.

Related

Creating a trigger with SELECT Function

Good day everyone,
I'm having trouble making my trigger work. As far as the functionality of the body and how it behaves, it does exactly as I intended for it to behave. However, when I start to fire the trigger, it returns an error in which Triggers should not have a SELECT statement from inside the main body. I'm still fairly new to coding and how to materialize the idea in my head into code. Hopefully someone could point me in a right direction on how change the Trigger I would like to have as a final result. Please see below script.
Update: Expected result would be whenever a user UPDATE a row and INSERT a new record via the application or job being run in the background, S1_HOVER_REPORT column would be updated with the value from the SELECT script and use the data from the S1_HOVER case result.
(Edit: I have updated the details of the problem above, added the Table being used and Error return)
Table: SITE
Column Name Type
------------------------------
ID VARCHAR2(14)
NAME VARCHAR2(70)
TYPE_CODE VARCHAR2(2)
PARENT VARCHAR2(14)
S1_HOVER_REPORT VARCHAR2(14)
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
AFTER INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
BEGIN
UPDATE (SELECT S1.ID,
S1.NAME,
S1.TYPE_CODE,
S1.PARENT AS PARENT1,
S2.PARENT AS PARENT2,
S1.S1_HOVER_REPORT,
CASE
WHEN (S1.TYPE_CODE = 'H2') THEN S1.PARENT
WHEN (S1.TYPE_CODE = 'S1') THEN S2.PARENT
ELSE S1.ID
END AS S1_HOVER
FROM SITE S1,
(SELECT ID,
NAME,
PARENT,
TYPE_CODE
FROM site
WHERE type_code='H2') S2
WHERE S1.PARENT=S2.ID
OR S1.ID = S2.PARENT) S3
SET S3.S1_HOVER_REPORT = S3.S1_HOVER;
END;
Error returned when Trigger fired:
Error report -
SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table
ORA-06512: at "MES.S1_HOVER_REPORT", line 2
ORA-04088: error during execution of trigger 'MES.S1_HOVER_REPORT'
01779. 00000 - "cannot modify a column which maps to a non key-preserved table"
*Cause: An attempt was made to insert or update columns of a join view which
map to a non-key-preserved table.
*Action: Modify the underlying base tables directly.
(Update: I have included the updated trigger and it's now compiling without any issue, but I'm having errors whenever I try updating a record)
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
BEFORE INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
DECLARE
v_S1_HOVER_REPORT VARCHAR2(14);
BEGIN
SELECT CASE
WHEN (S1.TYPE_CODE = 'H2') THEN S1.PARENT
WHEN (S1.TYPE_CODE = 'S1') THEN S2.PARENT
ELSE (S1.ID)
END AS S1_HOVER
INTO v_S1_HOVER_REPORT
FROM SITE S1,
(SELECT ID,
NAME,
PARENT,
TYPE_CODE
FROM site
WHERE type_code='H2') S2
WHERE S1.PARENT=S2.ID
OR S1.ID = S2.PARENT;
:NEW.S1_HOVER_REPORT := v_S1_HOVER_REPORT;
END;
Error report -
SQL Error: ORA-04091: table MES.SITE is mutating, trigger/function may not see it
ORA-06512: at "MES.S1_HOVER_REPORT", line 4
ORA-04088: error during execution of trigger 'MES.S1_HOVER_REPORT'
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.
Firstly from the error message
An attempt was made to insert or update columns of a join view which map to a non-key-preserved table.
S3 is the veiw ( you are creating the view by doing a select inside an update statment). You can try and change this to have key preservation but I really wouldn't know how.
The error suggests updating the base tables not the view. So as mentioned in the comments :old and :new are your friend.
:OLD holds all the values of the table the trigger is created on BEFORE the update (null if insert)
:NEW holds all the values of the table the trigger is created on AFTER the update / insert.....
So if I understand what you want to do correctly you would need to...
declare a variable eg v_S1_hover_report
do your select returning whatever value you need into this variable
set the value in your site table by doing
:NEW.S1_HOVER_REPORT := v_S1_hover_report
By setting this value into the :NEW object when a commit happens it will be committed to the database. This completely removes the need for an update statement in the trigger.
You can also use :NEW.id in your select statement to filter it down to the record you are updating if it is helpfull
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
AFTER INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
v_test varchar2(10);
BEGIN
select 'Your value' into v_test from dual;
:new.s1_hover_report := v_test;
END;
OR
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
AFTER INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
v_test varchar2(10);
BEGIN
select 'Your value' into :new.s1_hover_report from dual;
END;

Oracle Trigger error "ORA-00942: table or view does not exist"

this is my trigger :
create trigger tampone_trigger
after insert on tamponi.numerotelpaziente
for each row
begin
IF ( :new.numerotelpaziente not in (
select numtel
from users))
then
insert into spaiati values (new.numtel);
end if;
end;
The table "Tamponi" does exists, and "numerotelpaziente" is one of the columns...
Table "USERS" also exists, and "numtel" is one of its columns...
Why on earth is giving me that one error?
The trigger is supposed to look for this new cellPhone number inserted into "Tamponi" and check if this number exists in "Users", if not it has to be add to the separated table "spaiati" , where there is a column for it..
It is perfectly connected to my personal database (i'm running my JAVAfx application onto it, and it works fine, i just need to create some triggers).
Report error -
ORA-00942: table or view does not exist
00942. 00000 - "table or view does not exist"
*Cause:
*Action:
If i use "on Tamponi" instead of giving the column aswell, the error becomes this one :
Report error -
ORA-04082: NEW or OLD references not allowed in table level triggers
04082. 00000 - "NEW or OLD references not allowed in table level triggers"
*Cause: The trigger is accessing "new" or "old" values in a table trigger.
*Action: Remove any new or old references.
if i use "on tamponi" the error is now this :
2/5 PL/SQL: Statement ignored
2/40 PLS-00405: subquery not allowed in this context
Errori: controllare il log del compilatore
Since this code will be in a trigger, you will want it to be as efficient as possible since it might be run very often. The code below should do what you are hoping to achieve with minimal context switching.
CREATE TRIGGER tampone_trigger
AFTER INSERT
ON tamponi
FOR EACH ROW
BEGIN
INSERT INTO spaiati
SELECT :new.numerotelpaziente
FROM DUAL
WHERE NOT EXISTS
(SELECT 1
FROM users u
WHERE u.numtel = :new.numerotelpaziente);
END;
/
You can try this -
create trigger tampone_trigger
after insert on tamponi
for each row
declare
v_flag boolean := false;
begin
for c in (select numtel from users)
loop
if :new.numerotelpaziente = c.numtel
then
v_flag := true;
exit;
end if;
exit when no_data_found;
end loop;
insert into spaiati values (new.numtel);
end;
I don't have a DB client available currently, but this should be close to what you want:
create trigger tampone_trigger
after insert on tamponi for each row
declare
v_exists number;
begin
select count(*) into v_exists from users u where u.numtel = :new.numerotelpaziente;
if (v_exists = 0) then
insert into spaiati values(:new:numerotelpaziente);
end if;
end;

Why does it raise a mutating table error?

I am attenting to create a trigger that before delete a row in the table CLUBS check if the column END_DATE is null, if it's null the row instead of deleting the row it replaces the value with sysdate and if is not null the row doesn't get deleted.
CREATE OR REPLACE TRIGGER TRANSFER_DATA
BEFORE DELETE ON CLUBS
FOR EACH ROW
BEGIN
IF :old.END_DATE IS NULL
THEN UPDATE CLUBS SET END_DATE = SYSDATE;
ELSE
RAISE_APPLICATION_ERROR(-20033, 'No se puede borrar');
END IF;
END;
I've tried this code but it raises an error:
"table %s.%s is mutating, trigger/function may not see it"
How can I make it work?
By the way, I am working on Oracle sqldeveloper
You need to create a view and a INSTEAD OF trigger:
create or replace view v_CLUBS as
SELECT * FROM CLUBS; -- perhaps add 'WHERE END_DATE IS NULL'
CREATE OR REPLACE TRIGGER TRANSFER_DATA
INSTEAD OF DELETE ON V_CLUBS
FOR EACH ROW
BEGIN
IF :old.END_DATE IS NULL THEN
:new.end_date := sysdate;
ELSE
RAISE_APPLICATION_ERROR(-20033, 'No se puede borrar');
END IF;
END;
Processing through a view and an instead of trigger is differently an effective way to go. It seems your prior attempt approached the task incorrectly. A simple view is fully updateble (generally). But in this case you want to intercept the delete and apply the action to the underlying table. Assuming CLUBS is view you process against and CLUBS_TBL is the actual table the trigger you want is:
-- create trigger to process deletes on the view
create or replace trigger del_clubs_isd
instead of delete on clubs
begin
update clubs_tbl
set end_date = sysdate
where id = :old.id;
end del_clubs_isd;
See a full script at fiddle.
Further, as presented the user never needs to know the club is not actually deleted, but just marked to appear that way.

Trigger has mutating error when insert

I am having an issue with a trigger. After I add the trigger to my database, when I try to insert a row that the trigger will act on, I get a mutating table error. I don't know how to avoid this. Would someone mind looking at this and tell me what I am doing wrong, and how to rewrite it?
CREATE OR REPLACE TRIGGER ORNG_INV_LINE_TOTAL_TRIGGER
FOR INSERT OR UPDATE ON ORNG_INV_LINE
COMPOUND TRIGGER
AFTER EACH ROW IS
BEGIN
UPDATE ORNG_INVOICE SET Inv_Amount = (SELECT SUM(ORNG_INV_LINE.Inv_Line_Total)
FROM ORNG_INV_LINE
WHERE ORNG_INVOICE.INV_Num = :NEW.INV_Num);
END AFTER EACH ROW;
END ORNG_INV_LINE_TOTAL_TRIGGER;
/
I'm not sure why it is triggering an error. I'm trying to do the action after the update. All I want to do is get the sum for all of the lines of all matching invoice numbers, and write that value in the INVOICE table. Thanks for the help.
Your trigger is written on the table ORNG_INV_LINE for insert or update and again you made a select on the same table while updating ORNG_INVOICE table so trigger is mutating,
to over come this you have to use statement level trigger instead of row level trigger .
i.e after each statement should be there in code instead of after each row.
This can help you.
Here is the solution...apparently everyone is busy with Thanksgiving. Hopefully this helps the next guy.
create or replace TRIGGER ORNG_INV_L_TTL_TRIG
AFTER INSERT OR UPDATE OR DELETE ON ORNG_INV_LINE
BEGIN
UPDATE ORNG_INVOICE SET Inv_Amount = (SELECT SUM(Inv_Line_Total)
FROM ORNG_INV_LINE
WHERE INV_Num = ORNG_INVOICE.INV_Num);
END ORNG_INV_L_TTL_TRIG;
/

Oracle Trigger on Insert or Delete or Update

Trying to create an Oracle trigger that runs after a table is updated in any way. I've been googling this all morning and came up with this:
CREATE OR REPLACE TRIGGER gb_qty_change
AFTER UPDATE OR INSERT OR DELETE ON F_ITEM_STORE
FOR EACH ROW
DECLARE
v_qty V_AD_ON_HAND%rowtype;
v_isbn TD_ITEM_DESCRIPTION.TD_IDENTIFIER%type;
BEGIN
delete from gb_transaction where gb_tide = :new.ITST_ITEM_TIDE_CODE;
select TD_IDENTIFIER INTO v_isbn from TD_ITEM_DESCRIPTION where TD_TIDE = :new.ITST_ITEM_TIDE_CODE;
select * INTO v_qty from V_AD_ON_HAND where ITST_ITEM_TIDE_CODE = :new.ITST_ITEM_TIDE_CODE;
insert into gb_transaction(gb_tide, gb_isbn, gb_used_on_hand, gb_new_on_hand)
values(:new.ITST_ITEM_TIDE_CODE, v_isbn, v_qty.USED_ON_HAND, v_qty.NEW_ON_HAND);
END;
/
I'm trying to keep it to a single record per TIDE_CODE in the new table.
V_AD_ON_HAND is a view that pulls an inventory count.
gb_transaction is my new table where I'm logging these events
Comparing it to other peoples code it looks like it should run but I'm getting "Warning: Trigger created with compilation errors."
The problem, I believe is with the :new designations for a delete trigger. There is, after all, no NEW value as the record is expunged. You can only access the :OLD values on delete.
if you section the trigger by operation, you can do this.
CREATE OR REPLACE TRIGGER gb_qty_change
AFTER UPDATE OR INSERT OR DELETE ON F_ITEM_STORE
FOR EACH ROW
DECLARE
v_qty V_AD_ON_HAND%rowtype;
v_isbn TD_ITEM_DESCRIPTION.TD_IDENTIFIER%type;
BEGIN
IF INSERTING or UPDATING then
... insert your existing code
ELSE
... do something similar with the :old values for the deleting case
end if;
END;
/
Incidentally, it is generally helpfull if you tell us WHAT the error is, not just that you had one. If compiling via SQL*Plus script, after the forward slash call to compile the trigger after the "end;" statement, add a line that says:
SHOW ERRORS TRIGGER YOUR_TRIGGER_NAME;