create trigger to save CONSTRAINTs changes on tables in oracle - oracle

I want to create table to save all CONSTRAINTs changes in my oracle database,
so i have created this table(table name , constraint name , date , mode like [insert|update|delete] )
CREATE TABLE CONS
(
C_ID NUMBER NOT NULL
, C_NAME VARCHAR2(50) NOT NULL
, T_NAME VARCHAR2(50) NOT NULL
, EXE_DATE DATE NOT NULL
, MODE VARCHAR2(50) NOT NULL
);
the problem was by insert data,
I was thinking of creating trigger on user_cons_columns after insert or update or delete,
but I found that user_cons_columns is a view and i can't create trigger on it,
so how can I do this work?
or what is the tables that I can create trigger on it to do this???
thanks .......

User_Cons_Columns is metadata view in oracle and you can not use it for monitoring constraint changes.
I think, You can use this metadata view :
SELECT *
FROM ALL_CONSTRAINTS T
It shows which constraint has changed by LAST_CHANGE Column and other data that you can use them.

I don't know if we can create DML TRIGGERs on data dictionary tables in Oracle, but that sounds like a bad idea.
My suggestion to you would be to create a DDL Trigger on the ALTER event
Firstly, as a one time activity, you could store all the available constraints in your CONS table.
Then in your trigger, you could use these conditions to check if a table and column was altered.
if (ora_sysevent = 'ALTER' and
ora_dict_obj_type = 'TABLE')
then alter_column :=
ora_is_alter_column('FOO');
end if;
You could then query the user_cons_columns , ALL_CONSTRAINTS and CONS - whichever you find is relevant to store your data - to find if a new constraint was added or not. If it was indeed added or modified, make an entry into CONS or else update it ( using MERGE statement )

As you said, you can't create that type of a trigger on USER_CONS_COLUMNS:
SQL> create or replace trigger trg_myucc
2 before insert
3 on user_cons_columns
4 begin
5 null;
6 end;
7 /
create or replace trigger trg_myucc
*
ERROR at line 1:
ORA-25001: cannot create this trigger type on this type of view
Why wouldn't we try INSTEAD OF TRIGGER?
SQL> create or replace trigger trg_myucc
2 instead of insert
3 on user_cons_columns
4 for each row
5 begin
6 null;
7 end;
8 /
on user_cons_columns
*
ERROR at line 3:
ORA-01031: insufficient privileges
OK, that won't work either. But, what prevents us on creating a view upon a view, and then create INSTEAD OF trigger on that newly created view?
SQL> create or replace view my_ucc as select * from user_cons_columns;
View created.
SQL> create or replace trigger trg_myucc
2 instead of insert
3 on my_ucc
4 for each row
5 begin
6 null;
7 end;
8 /
Trigger created.
Fine; now you have a way to do what you're supposed to do. More about triggers here.

Related

adding a sequence to an existing table

i created a table but i forgot to add a sequence to one of the PK, its a sequence on a form page, i just cant find anything about it, is it possible or do i have to do the form all over again.
i tried to replace the PK but it doesnt give me the option to add the sequence when creating a new one.
i searched everywhere and asked the support in chat (didn't really help since its not their job).
all i could find was this and this.
I'd suggest you to skip Apex in this matter and do the following: presume this is your table:
SQL> create table test
2 (id number constraint pk_test primary key,
3 name varchar2(20)
4 );
Table created.
This is the sequence:
SQL> create sequence myseq;
Sequence created.
As you forgot to specify PK source while creating Apex Form page, never mind - let the database handle it. How? Create a BEFORE INSERT trigger:
SQL> create or replace trigger trg_bi_test
2 before insert on test
3 for each row
4 when (new.id is null)
5 begin
6 :new.id := myseq.nextval;
7 end trg_bi_test;
8 /
Trigger created.
Let's test it: I'm inserting only the NAME (which is what your Apex Form will be doing):
SQL> insert into test (name) values ('Littlefoot');
1 row created.
What is table's contents?
SQL> select * from test;
ID NAME
---------- --------------------
1 Littlefoot
SQL>
See? Trigger automatically inserted ID (primary key) column value.
If it were an Interactive Grid (which lets you insert several records at a time):
SQL> insert into test (name)
2 select 'Bigfoot' from dual union all
3 select 'FAD' from dual;
2 rows created.
SQL> select * from test;
ID NAME
---------- --------------------
1 Littlefoot
2 Bigfoot
3 FAD
SQL>
Works just fine.
And what's another benefit: you don't have to modify Apex application at all.

Oracle DB. Insert Trigger with Merge statament inside. Table is mutating

I have two back-end systems (the old one and the new one) that shares an Oracle DB.
In the older system, to save customers data, there are two tables
customers_A
ID NAME ETC
1 PETE ....
customers_B
ID NAME ETC
1 JOSH ...
2 ROSS ...
In the new system I've created a new table called All_Costumer, to join those tables.
This new table contains customer ID's of type A and B respectively.
All_Customers
ID ID_CUSTOMER_A ID_CUSTOMER_B
A19E----D2B0 1 null
A19E----D2B1 null 1
A19E----D2B2 null 2
So, when the new system creates a new customer of type A, data are inserted on customer_A and All_Customers tables, with customer of type B as well.
Currently, the old system is working too, and when a new customer of type A is created, data is inserted only on customer_A table, but I need that data in All_Customers too.
To solve this, I've created a TRIGGER with a MERGE INTO statement inside, to insert a row in All_Customers if doesn't exist on this table (when a new customer of type A are created by the older system)
CREATE OR REPLACE TRIGGER customers_trg
AFTER INSERT
ON customer_A
FOR EACH ROW
DECLARE
variables that doesn't matters
BEGIN
MERGE INTO all_customers
USING (SELECT :new.id id FROM customer_A where id = :new.id) customer
ON (all_customers.id_customer_a = customer.id)
WHEN NOT MATCHED THEN
INSERT (id, id_customer_a)
VALUES (SYS_GUID(), :new.id, null);
COMMIT;
END;
But when I try to create a new customer from the older system, I get this error:
ORA-04091: table **customer_A** is mutating, trigger/function may not see it
Any idea to solve this?
I've tried adding PRAGMA AUTONOMOUS_TRANSACTION; on DECLARE section, but didn't work.
Note: I can't modify the old system
The immediate issue is that you're querying table_a in a trigger against that table; but you don't need to. Your merge query
SELECT :new.id id FROM customer_A where id = :new.id
can simply do
SELECT :new.id id FROM dual
i.e. the clause becomes:
...
USING (SELECT :new.id id FROM dual) customer
ON (all_customers.id_customer_a = customer.id)
...
You also can't commit in a trigger - unless it's autonomous, which this shouldn't be. You said you'd tried that, but it breaks if the insert is rolled back, since the merged row will still exist. So hopefully that commit is just a hang-over from trying and rejecting that approach.
But it works in this db<>fiddle, anyway.
If you weren't adding the GUID you could get the same effect with a view:
create or replace view all_customers (id_customer_a, id_customer_b) as
select id, null from customers_a
union all
select null, id from customers_b;
db<>fiddle

creating mview containing pl/sql definitions in with clause

How to use pl sql definitions in with clause while creating mview?
An explanation with example would really help.
Also I have tried the following queries and I have faced some errors.
create table tab1
(
fname varchar2(15) ,
lname varchar2(15)
);
create or replace type user_data_type as object (val1 varchar2(20),val2 varchar2(20));
/
CREATE OR REPLACE Function get_fname_lname(n in varchar2)
RETURN user_data_type as
name user_data_type;
cursor buffer_cur is
select fname,lname from tab1 where fname=n;
BEGIN
name := new user_data_type(null,null);
OPEN buffer_cur;
fetch buffer_cur into name.val1,name.val2;
close buffer_cur;
return name;
End;
/
select * from tab1;
select get_fname_lname(fname).val1 fname,get_fname_lname(fname).val2 from tab1;
drop materialized view mv1;
create materialized view mv1
with names as
(select get_fname_lname(fname).val1 fname,get_fname_lname(fname).val2 lname from tab1)
select fname,lname from names;
select * from mv1;
I have seen this error:
Error while creating mview:
ORA-00905: missing keyword
Can some one point me out what might be the issue.
You can try something like this while creating view , since recursive views are not supported in oracle.
By using with clause you are already creating an inline view and for that view you are trying to create a materialised view which is not supported in oracle
create materialized view mv1
as
select fname,lname from
(select get_fname_lname(fname).val1 fname,get_fname_lname(fname).val2 lname
from tab1);

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

Oracle view not updatable, advice on Instead Of triggers

after migrating a system/database we modified a central table which has been used for interfacing with 15 different systems. We used this migration to add and delete a few fields in this table.
To maintain direct compatibility with the interfacing systems (i.e. only need to change the database-link), a view has been created which shows the exact same columns as the old table had. However, some of these columns are only emulated, so the view contains constructs like these:
(...)
CREATE OR REPLACE VIEW STAFF_DATA_COMPAT AS
SELECT
NVL(knownas_surname,surname) as surname,
first_name
middle_name as mid-name
NULL as ni,
NULL as home_tel_no,
(...)
Obviously, this view is not inherently updatable.
I do understand, that you need INSTEAD OF triggers for all DML (insert, update, delete) statements.
I can see, that a INSTEAD OF INSERT trigger should be quite straightforward (just inserting :NEW.field to the real table, where appropriate and ignoring the others).
But the actual question: How to write the according INSTEAD OF UPDATE/DELETE triggers? For instance, how do I take over the "WHERE" clause of an original DELETE statement? Is there anything else I should worry about, any side-effects when using these triggers?
Btw. It's Oracle 11g.
The INSTEAD OF trigger would look like this (I've assumed you have a primary key column id):
SQL> CREATE OR REPLACE TRIGGER trg_staff_data_cpt_instead_upd
2 INSTEAD OF UPDATE ON staff_data_compat
3 FOR EACH ROW
4 BEGIN
5 UPDATE staff_data_compat_t
6 SET knownas_surname = :new.surname,
7 first_name = :new.first_name,
8 middle_name = :new.mid_name
9 WHERE id = :new.id
10 END;
11 /
Trigger created
Note that some columns may in fact be updatable in the original view. Query the all_updatable_columns view (before creating the trigger) to find out:
SQL> CREATE TABLE staff_data_compat_t AS
2 SELECT object_name knownas_surname,
3 owner surname,
4 object_type first_name,
5 subobject_name middle_name
6 FROM all_objects;
Table created
SQL> CREATE OR REPLACE VIEW staff_data_compat AS
2 SELECT
3 NVL(knownas_surname,surname) as surname,
4 first_name,
5 middle_name mid_name,
6 NULL as ni,
7 NULL as home_tel_no
8 FROM staff_data_compat_t;
View created
SQL> SELECT * FROM all_updatable_columns WHERE table_name = 'STAFF_DATA_COMPAT';
OWNER TABLE_NAME COLUMN_NAME UPDATABLE INSERTABLE DELETABLE
------ ------------------ ------------ --------- ---------- ---------
VNZ STAFF_DATA_COMPAT SURNAME NO NO NO
VNZ STAFF_DATA_COMPAT FIRST_NAME YES YES YES
VNZ STAFF_DATA_COMPAT MID_NAME YES YES YES
VNZ STAFF_DATA_COMPAT NI NO NO NO
VNZ STAFF_DATA_COMPAT HOME_TEL_NO NO NO NO
If you only need to insert/update these columns, you don't need an INSTEAD OF trigger.
INSTEAD OF triggers are implicitly "FOR EACH ROW", so you don't have to find out the WHERE clause, you just do something like this:
begin
delete base_table
where pk = :old.pk;
end;
This also shows one of the drawbacks of INSTEAD OF triggers: they work row-by-row not in sets.

Resources