Posted a question a couple of days ago and successfully got my trigger to work!
But having a few new problems.
I have two tables:
CREATE TABLE "ASSESSMENT"
( "ASSESSMENT_NAME" VARCHAR2(50) NOT NULL ENABLE,
"DEADLINE_DATE" DATE NOT NULL ENABLE,
CONSTRAINT "ASSESSMENT_PK" PRIMARY KEY ("ASSESSMENT_NAME") ENABLE
)
CREATE TABLE "ASSESSMENT_ANNOUNCEMENT"
( "ASSESSMENT_NAME" VARCHAR2(50) NOT NULL ENABLE,
"DEADLINE_DATE" DATE NOT NULL ENABLE,
"ATTENTION" VARCHAR2(500) NOT NULL ENABLE,
CONSTRAINT "ASSESSMENT_ANNOUNCEMENT_PK" PRIMARY KEY ("ASSESSMENT_NAME") ENABLE
)
The trigger I have:
CREATE OR REPLACE TRIGGER "TEST"
AFTER INSERT OR UPDATE OR DELETE
ON ASSESSMENT
FOR EACH ROW
BEGIN
IF :new.DEADLINE_DATE >= SYSDATE - 7
THEN
INSERT INTO ASSESSMENT_ANNOUNCEMENT(ASSESSMENT_NAME, DEADLINE_DATE ,ATTENTION)
VALUES(:new.ASSESSMENT_NAME, :new.DEADLINE_DATE, 'DEADLINE IS 7 DAYS OR LESS!');
END IF;
END;
Insert works correctly across the tables. But, when I update on the ASSESSMENT table, a new row is
inserted in the ASSESSMENT_ANNOUNCEMENT table - it is not updated.
A delete from the ASSESSMENT table removes the row from ASSESSMENT table but not the entry from
the ASSESSMENT_ANNOUNCEMENT table.
Any help and/or guidance would be fantastic!
If you want to update or delete a row in ASSESSMENT_ANNOUNCEMENT, you should do it explicitly using update or delete statement.
Use the following construction in your trigger:
IF INSERTING THEN
-- actions for inserting
ELSIF UPDATING THEN
-- actions for updating
ELSE
-- actions for deleting
END IF;
Just to give you a complete sample
CREATE OR REPLACE TRIGGER "TEST"
AFTER INSERT OR UPDATE OR DELETE
ON ASSESSMENT
FOR EACH ROW
BEGIN
IF INSERTING THEN
IF :new.DEADLINE_DATE >= SYSDATE - 7
THEN
INSERT INTO ASSESSMENT_ANNOUNCEMENT(ASSESSMENT_NAME, DEADLINE_DATE ,ATTENTION)
VALUES(:new.ASSESSMENT_NAME, :new.DEADLINE_DATE, 'DEADLINE IS 7 DAYS OR LESS!');
END IF;
ELSIF UPDATING THEN
UPDATE ASSESSMENT_ANNOUNCEMENT SET
ASSESSMENT_NAME=:new.ASSESSMENT_NAME,
DEADLINE_DATE=:new.DEADLINE_DATE,
ATTENTION='Deadline Updated'
WHERE ASSESSMENT_NAME=:old.ASSESSMENT_NAME;
ELSE
DELETE ASSESSMENT_ANNOUNCEMENT
WHERE ASSESSMENT_NAME=:old.ASSESSMENT_NAME;
END IF;
END;
Depending on your real business logic and size of PL/SQL code, it may be more clear to create three triggers
CREATE OR REPLACE TRIGGER "TEST_AI_TRG" AFTER INSERT ON ASSESSMENT ...
CREATE OR REPLACE TRIGGER "TEST_AU_TRG" AFTER UPDATE ON ASSESSMENT ...
CREATE OR REPLACE TRIGGER "TEST_AD_TRG" AFTER DELETE ON ASSESSMENT ...
Related
I'm going to create a trigger to audit a table. Suppose my table define as below.
COUNTRY_ID NOT NULL CHAR(2)
COUNTRY_NAME VARCHAR2(40)
REGION_ID NUMBER
and my log table created as below.
create table country_log(
username varchar2(10),
transaction_date date,
new_value varchar(20),
old_value varchar(20)
)
My half completed trigger would be like below.
CREATE OR REPLACE TRIGGER tr_countryTable
AFTER UPDATE ON COUNTRIES
FOR EACH ROW
BEGIN
insert into country_log (username,transaction_date,new_value,old_value ) values (USER, sysdate,**:New, :Old** );
END;
/
I need to know instead of comparing each column value in :old and :new, how to get exactly updated column's new and old values.
In an UPDATE trigger, a column name can be specified with an UPDATING predicate to determine if the named column is being updated. Let's define a trigger as the following:
CREATE OR REPLACE TRIGGER tr_countryTable
AFTER UPDATE ON COUNTRIES
FOR EACH ROW
BEGIN
IF UPDATING ('COUNTRY_NAME') THEN
-- :new.COUNTRY_NAME is new value
-- :old.COUNTRY_NAME is old value
END IF;
IF UPDATING ('REGION_ID') THEN
-- :new.REGION_ID is new value
-- :old.REGION_ID is old value
END IF;
END;
/
Oracle 11g triggers documentation
I am looking for a generic procedure that will generate audit trails for Oracle databases. We are currently using a similar procedure on SQL Server and wondering if an Oracle equivalent exists. We are hoping the audit table will be a separate table than the original table and include user/date time info.
Here is the SQL Server equivalent we are using: https://www.codeproject.com/Articles/21068/Audit-Trail-Generator-for-Microsoft-SQL
Any advice is greatly appreciated.
If you don't want to use Oracle native mechanism, you could have your very own framework that generates and reads your own auditing table (I know you can, we had similar thing where I once worked).
Here are the main components:
a_sqnc is the sequence you will use in TrackTable to keep track of the order of actions in column NO_ORD (even though there is also a D_UPD column with the modification time).
create sequence a_sqnc
minvalue 1
maxvalue 99999999
start with 1
increment by 1
nocache;
TrackTable will have a TABLE_NAME column in order to track changes from different tables. It also have a PK_VALUE and ROW_VALUE where we store the data that changed. Here is the table creation with useful indexes:
create table TrackTable (
table_name VARCHAR2(50) not null,
action VARCHAR2(240) not null,
no_ord NUMBER(12) not null,
nature VARCHAR2(3) not null,
pk_value VARCHAR2(4000),
row_value VARCHAR2(4000),
ori VARCHAR2(250),
c_user VARCHAR2(20),
d_upd DATE
);
create index AP_D_UPD on TrackTable (D_UPD);
create index AP_NO_ORD on TrackTable (NO_ORD);
create index AP_TABLE_NAME on TrackTable (TABLE_NAME);
Say you have a simple table BANK with two columns PK_val (the primary key) and val:
create table BANK (
pk_val VARCHAR2(50) not null,
val VARCHAR2(240) not null
);
alter table BANK
add constraint BK_PK primary key (pk_val)
using index ;
Use DBMS_APPLICATION_INFO.READ_MODULE(w_sess_mod, w_sess_act) to know what module and what action operates: I concatenate both in column ORI in TrackTable;
user Oracle session variable will allow you tracking who did the change in column c_user;
Here is how to create trigger TRCK_BNK to track changes in table BANK; it will categorize in 3 actions: DELETE, UPDATE, INSERT (you can remove the INSERT case if needed).
CREATE OR REPLACE TRIGGER "TRCK_BNK"
AFTER DELETE OR INSERT OR UPDATE
ON BANK
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
w_a VARCHAR2(10);
W_ERRM VARCHAR2(1000);
W_CODE VARCHAR2(1000);
w_n VARCHAR2(200) := 'BANK';
w_id NUMBER := a_sqnc.nextval;
w_act v$session.action%type;
w_mod v$session.module%type;
w_ori TrackTable.ORI%TYPE;
BEGIN
DBMS_APPLICATION_INFO.READ_MODULE(w_mod, w_act);
w_ori := 'Module : '||w_mod ||' ; Action : '||w_act;
----------------------------------
-- test which action is for change
----------------------------------
IF UPDATING
THEN
w_a := 'UPDATE';
ELSIF DELETING
THEN
w_a := 'DELETE';
ELSIF INSERTING
THEN
w_a := 'INSERT';
END IF;
----------------------------------
-- Insert into TrackTable
----------------------------------
If w_a in ('UPDATE', 'DELETE') then
Insert into TrackTable
Select w_n, w_a, w_id, 'OLD', :OLD.pk_val, :OLD.val
, w_ori, user, sysdate
From Dual;
End if;
-- if you update, there is a new value and an old value
If w_a in ('UPDATE', 'INSERT') then
Insert into TrackTable
Select w_n, w_a, w_id, 'NEW', :NEW.pk_val, :NEW.val
, w_ori, user, sysdate
From Dual;
End if;
Exception
When others then
Begin
W_ERRM := SQLERRM;
W_CODE := SQLCODE;
-- try inserting in case of error anyway
Insert into TrackTable
Select w_n, w_a, -1, 'ERR', 'Grrr: '||W_CODE, W_ERRM
, w_ori, user, sysdate
From Dual;
End;
End;
/
Then add functions to your framework that generates the triggers given a table, retrieves changes, reverts table to a given date...
NB:
This way of tracking every change on the table impairs performances if table changes a lot. But it is great for parameter tables that scarcely change.
Have a look at Oracles Flashback Data Archive which bases upon the UNDO Data. It can be configured to track any change to your data. It is available in any edition of oracle since 11g2 (11.2.0.4).
In the Oracle documentation it says that optimazation is limited but basic functionality is available in any edition.
I have 2 tables 'label' and 'musician'
CREATE TABLE label
(labId varchar(10) NOT NULL PRIMARY KEY,
labName varchar(20) NOT NULL
);
CREATE TABLE musician
(musId varchar(10) NOT NULL PRIMARY KEY,
musName varchar(30) NOT NULL,
labId varchar(10) NOT NULL,
CONSTRAINT MusLabel FOREIGN KEY (labId) REFERENCES label(labId)
);
I created a trigger to limit the number of musicians a label can have within a
range of 1 to 5; so that for example a label x cannot have 6 musicians:
CREATE OR REPLACE TRIGGER before_musician_insert
BEFORE INSERT ON musician
FOR EACH ROW
DECLARE
total integer;
BEGIN
SELECT COUNT(*) INTO total
FROM musician, label
WHERE musician.labId=label.labId;
IF (total < 0 OR total > 5)
THEN
DBMS_OUTPUT.PUT_LINE('Invalid');
END IF;
END;
/
When I insert a 6th musician into the table with the same label ID, the insert statement does not 'trigger' the TRIGGER and the 6th value is added to the table.
I don't know how to fix this.
I tried a check constraint but with varchar values, it is not working either.
I appreciate your help.
Your code has multiple issues. For instance, it is not accessing :new. The trigger is on the wrong table. It has no error generation.
I might suggest something like this:
CREATE OR REPLACE TRIGGER before_labels_insert
BEFORE INSERT ON labels
FOR EACH ROW
DECLARE
v_total integer;
user_xcep EXCEPTION;
PRAGMA EXCEPTION_INIT( user_xcep, -20001 );
BEGIN
SELECT COUNT(*) INTO v_total
FROM labels l
WHERE l.labId = :new.labId;
IF (v_total >= 5) THEN
DBMS_OUTPUT.PUT_LINE('Invalid');
RAISE user_xcep
END IF;
END;
Your trigger fires BEFORE the insert, so the sixth entry doesn't exist when the query is executed.
If you want to error on the insert of the 6th entry, you can make it an AFTER trigger (and fire once per statement, rather than FOR EACH ROW).
I have to make a trigger that fills the primary key field user_id from a sequence.
So I did this first:
Create sequence user_seq;
Then I created the trigger. I also had to make the function that checks to see if the values in emp_id for the table users in the schema system_users exists in the table employees for the schema HRM_REPO. This part is in comments because it caused major issues.
create or replace trigger user_trig_bi
Before insert on system_users.users
for each row
begin
select user_seq.nextval
into new.user_ID
From dual;
/*where exists (SELECT emp_id
from hrm_repo.employees
where hrm_repo.employees.emp_id = system_users.users.emp_id);*/
END;
I get the error that new.user_id must be defined.
If I do run the where exists part, I get the notification that system_users.users.emp_id is an invalid identifier.
Please help me!
Regards,
Vinny
Replace new with :new:
create or replace trigger user_trig_bi
Before insert on system_users.users
for each row
begin
select user_seq.nextval
into :new.user_ID
From dual;
END;
or simply:
create or replace trigger user_trig_bi
Before insert on system_users.users
for each row
begin
:new.user_ID := user_seq.nextval;
END;
Dmitry got the answer already. As an addition: you should never try to check referential integrity using triggers, it will not work correctly. You should add foreign key (system_users.users.emp_id) references hrm_repo.employees(emp_id) instead. See http://docs.oracle.com/cd/B10500_01/server.920/a96524/c22integ.htm if you missed it before.
this is my very first question, I'm new here so I hope I'm asking well in the right topic... etc.
I'm trying with a trigger wich catch information from one table, compares some data from this table and if a condition is true then the trigger have to store the information changing some values. There are my tables and the trigger. I need this trigger to store larger tables with a lot of columns and a lot more information but this is just a test, please help! :'(
create table datos_prueba(
nombre varchar2(10) primary key,
numero number(3),
mensaje varchar(30),
fecha date);
create table store_datos_prueba(
nombre varchar(20) primary key,
numero number(5),
mensaje varchar(30),
fecha date);
And this is the trigger, I worte it but it's wrong...
create or replace trigger tgr_trigger_prueba
after insert on datos_prueba
for each row
declare
cambio_numero number;
begin
if :datos_prueba.numero <= 5 then
insert into store_datos_prueba (nombre,numero,mensaje,fecha) values(:datos_prueba.nombre,666,:datos_prueba.mensaje,:datos_prueba.fecha);
else
insert into store_datos_prueba (nombre,numero,mensaje,fecha) values(:datos_prueba.nombre,777,:datos_prueba.mensaje,:datos_prueba.fecha);
end if;
end;
You have to use old or new to refer the row value. Not the table name.
create or replace trigger tgr_trigger_prueba
after insert on datos_prueba
for each row
declare
cambio_numero number;
begin
if :new.numero <= 5 then
insert into store_datos_prueba (nombre,numero,mensaje,fecha) values(:new.nombre,666,:new.mensaje,:new.fecha);
else
insert into store_datos_prueba (nombre,numero,mensaje,fecha) values(:new.nombre,777,:new.mensaje,:new.fecha);
end if;
end;
/
More on Triggers