I want to keep track of changes to one table in another table. What I need is an after update trigger which writes the name of changed column (if multiple columns are changed then there will be multiple inserts to the CHANGES table),the column's old and new values. How do I do that. I tried this but got an error after updating the table.So I'm giving you just the body.
IF :NEW.STAJYEAR!=:OLD.STAJYEAR THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJYEAR',:OLD.STAJYEAR,:NEW.STAJYEAR);
END IF;
the error code is :ORA-04098: trigger 'SYS.TR__TRACK_CHANGES' is invalid and failed re-validation
CREATE OR REPLACE TRIGGER STAJCHANGER.TR_TRACK_CHANGES
AFTER UPDATE
OF STAJYEAR
,STAJMONTH
,STAJDAY
ON STAJCHANGER.STAJ
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
OLDVALUE NUMBER;
NEWVALUE NUMBER;
COLUMNID NUMBER;
BEGIN
IF :NEW.STAJYEAR!=:OLD.STAJYEAR THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJYEAR',:OLD.STAJYEAR,:NEW.STAJYEAR);
END IF;
IF :NEW.STAJMONTH!=:OLD.STAJMONTH THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJMONTH',:OLD.STAJMONTH,:NEW.STAJMONTH);
END IF;
IF :NEW.STAJDAY!=:OLD.STAJDAY THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJDAY',:OLD.STAJDAY,:NEW.STAJDAY);
END IF;
END TR_TRACK_CHANGES;
/
The error appears to indicates that the trigger owner is SYS, but the creation statement you show explicitly gives the owner as STAJCHANGER.
This makes me wonder, did you accidentally create an (invalid) version of the trigger in SYS at some point, and forget to drop it?
This SQL Plus command will show the error:
SHOW ERROR TRIGGER STAJCHANGER.TR_TRACK_CHANGES
Related
I'm trying to write a trigger to update couple of columns in a table for that I wrote a function which is doing actual functionality and trigger to execute that function:
But I'm getting the exception can someone guide me what went wrong here:
Function I'm writing :
CREATE OR REPLACE FUNCTION myFunction()
RETURNS TRIGGER AS $$
BEGIN
IF OLD.timestamp IS NOT DISTINCT FROM NEW.timestamp THEN
NEW.lst_updt_ts = now();
END IF;
RETURN NEW;
END;
Trigger:
CREATE OR REPLACE TRIGGER Mytrigger
BEFORE UPDATE ON mytable
FOR EACH ROW
BEGIN
myFunction;
END;
When I'm compiling the function
below is the error I'm getting :
PLS-00103: Encountered the symbol ")" when expecting one of the following:
current delete exists prior
Compile error at line 1, column 34
The syntax is definitely not an Oracle. As far as I can understand, you need to update lst_updt_ts to current timestamp if user tries to update it manually. I'd try to write something like that
CREATE OR REPLACE TRIGGER Mytrigger
BEFORE UPDATE ON mytable
FOR EACH ROW
BEGIN
IF :OLD.timestamp <> :NEW.timestamp THEN
:NEW.lst_updt_ts = sysdate; -- or systimestamp;
END IF;
END;
The DBMS complains about you using an open parentheses here: myFunction( and then not having any parameters before closing with ). Remove the parentheses.
But then, Oracle doesn't support this trigger function syntax known from PostgreSQL. Neither does it support IS NOT DISTINCT FROM yet.
Unfortunately, you cannot even pass the row like this:
CREATE OR REPLACE FUNCTION myFunction(p_new IN OUT mytable.ROWTYPE%)
because Oracle doesn't treat :new and :old as row records. I've suggested this in Oracle Database Ideas last year. You can vote for it :-) Link: https://community.oracle.com/tech/apps-infra/discussion/comment/16785577#Comment_16785577
There are two ways that I see for now:
Put the code in your trigger directly.
Write a function getting :OLD.timestamp and :NEW.timestamp and :NEW.lst_updt_ts and returning the altered :NEW.lst_updt_ts.
I had trouble converting the following command to the oracle command.
I will be glad if you help!
Create Trigger sales_stock_reduction
On SalesMovements
After insert
as
Declare #ProductId int
Declare #Piece int
Select #ProductId=ProductId, #Piece=Piece from inserted
Update Uruns set stock=stock - #Piece where ProductId=#ProductId
In this code, when sales are made, the number of stocks in the product table is reduced through the sales movement table.
I could not write this code in oracle. Wonder how to write in Oracle
You can convert that like this
CREATE OR REPLACE TRIGGER sales_stock_reduction
AFTER INSERT ON SalesMovements
FOR EACH ROW
DECLARE
v_ProductId inserted.ProductId%type;
v_Piece inserted.Piece%type;
BEGIN
BEGIN
SELECT ProductId, Piece
INTO v_ProductId, v_Piece
FROM inserted;
EXCEPTION WHEN NO_DATA_FOUND THEN NULL;
END;
UPDATE Uruns
SET stock=stock - v_Piece
WHERE ProductId=v_ProductId;
END;
/
In Oracle :
OR REPLACE clause is used whenever the trigger needs to be edited
local variables might be defined as the data type of those have
within the table
each individual statements end with a semi-colon
exception handling for NO_DATA_FOUND is added due to presuming at most one
row returns from the current query for the inserted table which doesn't have
a WHERE condition to restrict the result set
Oracle APEX. I want to create trigger: if user deletes a row where ENDDATE is null the row won't be deleted overwise it will. This is my script:
CREATE OR REPLACE TRIGGER CHECK_NOT_NULL_
BEFORE DELETE ON CAREER
FOR EACH ROW
BEGIN
IF(OLD.ENDDATE IS NULL)
INSERT INTO CAREER VALUES (OLD.JOBNO, OLD.EMPNO, OLD.STARTDATE, OLD.ENDDATE);
END IF;
END CHECK_NOT_NULL_;
But I have ORA-24344 error. Can you explain why and what should I do to fix it?
Your trigger attempts to re-insert the row if the END_DATE is null. This won't work (you'll get the notorious mutating table error). But anyway, if you want to prevent deletion of the row it's simpler and clearer to simply do that:
CREATE OR REPLACE TRIGGER CHECK_NOT_NULL_
BEFORE DELETE ON CAREER
FOR EACH ROW
BEGIN
IF :OLD.ENDDATE IS NULL THEN
raise_application_error(-20000, 'Cannot delete a row when ENDDATE is null');
END IF;
END CHECK_NOT_NULL_;
This fails the action and tells the user why their action was refused. Silently undoing a user's action is bad practice, because it's mystifying, and mystified users are unhappy and often angry users.
Precede all olds with a colon :, i.e.
CREATE OR REPLACE TRIGGER CHECK_NOT_NULL_
BEFORE DELETE ON CAREER
FOR EACH ROW
BEGIN
IF(:OLD.ENDDATE IS NULL)
INSERT INTO CAREER VALUES (:OLD.JOBNO, :OLD.EMPNO, :OLD.STARTDATE, :OLD.ENDDATE);
END IF;
END CHECK_NOT_NULL_;
Also, I'd suggest you to name all columns you're inserting into, e.g.
insert into career (jobno, empno, startdate, enddate)
values (:old.jobno, :old.empno, :old.startdate, :old.enddate);
I have created a very simple trigger in a certain table (e.g. TABLE_TRIGGER) that will call a procedure (handles all the logic). This table has a column (e.g. AUDITID) which I would like to exclude in the trigger function, I mean, if update is only done in AUDITID column, the procedure should not be executed.
Below is the trigger:
CREATE OR REPLACE TRIGGER TABLE_TRIGGER_FUNCTION
AFTER INSERT OR UPDATE
ON TABLE_TRIGGER
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
-- procedure();
END;
Is there other way to do it, other than the one suggested here: Oracle: excluding updates of one column for firing a trigger
The answer provided here is the one I would like to have Fire trigger on updates (excluding certain fields change) but I am not sure if this is something that can also be done using Oracle PL/SQL syntax.
By the way, I have tried using the following WHEN statement by excluding the AUDITID column but the trigger did not work at all and the procedure was not executed.
WHEN (NEW.FILEID != OLD.FILEID OR
NEW.DESCRIPTION != OLD.DESCRIPTION OR
NEW.IMAGEID != OLD.IMAGEID OR
NEW.STATETYPEID != OLD.STATETYPEID OR
NEW.ACCESSLEVELID != OLD.ACCESSLEVELID OR
NEW.FILETYPEID != OLD.FILETYPEID)
I'm not aware of a way to exclude a column from an update trigger, but you could define a list of columns it would fire on, and list all the columns except the auditid:
CREATE OR REPLACE TRIGGER TABLE_TRIGGER_FUNCTION
AFTER INSERT OR UPDATE
OF FILEID, -- Field list starts here...
DESCRIPTION,
IMAGEID,
STATETYPEID,
ACCESSLEVELID,
ACCESSLEVELID,
FILETYPEID -- ... and ends here
ON TABLE_TRIGGER
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
-- procedure();
END;
During insets ,all the references to OLD for insert event will result in NULL hence your procedure is not gonna run.
Consider and refer the below code blocks:
I think this is something you are looking for.
create table hr.test(id1 int,id2 int);
create or replace procedure hr.test_proc
as
begin
dbms_output.put_line('trigger');
end;
CREATE OR REPLACE TRIGGER test_trg
AFTER INSERT OR UPDATE
ON hr.test
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
if updating and :old.id1 !=:new.id1 then
null;--do nothing just as you want
else
hr.test_proc;--call your proc
end if;
END;
CREATE TRIGGER Background_Process_Report_trit
AFTER INSERT
ON Background_Process_Report
FOR EACH ROW
IF INSERT(PROCESS_NAME)
BEGIN
SET EXECUTION_TIMESTAMP := NEW.TIMESTAMP;
END;
/
process_name -- column in my Background_Process_Report table.but i want to update the each time the process_name is created(by java application), trigger update the time in the EXECUTION_TIMESTAMP table.
but it is throwing the compliation error..
error:
IF INSERT(PROCESS_NAME)
*
ERROR at line 5:
ORA-04079: invalid trigger specification
how to reslove this error
If EXECUTION_TIMESTAMP is a table as you say, then it must have a column you want to update, let's call it TIMESTAMP_COL. The the trigger would be something like:
CREATE TRIGGER Background_Process_Report_trit
AFTER INSERT
ON Background_Process_Report
FOR EACH ROW
WHEN (NEW.PROCESS_NAME IS NOT NULL)
BEGIN
UPDATE EXECUTION_TIMESTAMP
SET TIMESTAMP_COL = NEW.TIMESTAMP
WHERE ???; -- Change ??? to the appropriate condition
END;
/
I have assumed that by "IF INSERT(PROCESS_NAME)" you mean "if a non-null value is inserted into PROCESS_NAME" and created a WHEN clause to match.