Compound Trigger to turn a single row into a multirow - oracle

`Basically, i need to create something that when the user insert a value on a table, it turns into a multi insert, he will say the number of rows that will become in a part of table.
But i have 0 ideia from were begin.
My table have COMPMOV - INT: is the number of rows will return.
VLRPROV - NUMBER:
DTVENC - DATE:
PRAZO - INT:
PARCELA - INT:
i started from a another trigger that i created before, but this one i just fill a field that is blank when the user insert.`
CREATE OR replace TRIGGER "TRG_INC_USU_OBSMULTI"
FOR INSERT OR UPDATE
ON AD_OBSMULTI
COMPOUND TRIGGER TYPE R_OBS_TYPE IS RECORD (
OBS_ID AD_OBSMULTI.ID%TYPE,
OBS_IDOBS AD_OBSMULTI.IDOBS%TYPE,
OBS_CODUSU AD_OBSMULTI.CODUSU%TYPE
);
TYPE T_OBS_TYPE IS TABLE OF R_OBS_TYPE INDEX BY PLS_INTEGER;
T_OBS T_OBS_TYPE;
AFTER EACH ROW IS BEGIN
T_OBS (T_OBS.COUNT + 1).OBS_IDOBS := :NEW.IDOBS;
T_OBS (T_OBS.COUNT).OBS_ID := :NEW.ID;
T_OBS (T_OBS.COUNT).OBS_CODUSU := :NEW.CODUSU;
END AFTER EACH ROW;
AFTER STATEMENT IS L_CODUSU AD_OBSMULTI.CODUSU%TYPE;
BEGIN
SELECT
STP_GET_CODUSULOGADO INTO L_CODUSU
FROM
DUAL;
FOR indx IN 1..T_OBS.COUNT
LOOP
IF T_OBS(indx).CODUSU IS NULL THEN
UPDATE
AD_OBSMULTI
SET
CODUSU = L_CODUSU
WHERE
ID = T_OBS(indx).OBS_ID
AND IDOBS = T_OBS(indx).OBS_IDOBS;
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/

A trigger can modify the values of the dml statement (insert/update/delete) only. An additional transaction on the same table within a trigger on that table is generally a bad idea.
But here is a workaround.
Create a table
Create a view on that table
Create an instead of trigger on the view
Insert into the view
Example:
CREATE TABLE MAIN_TABLE (id number);
Table MAIN_TABLE created.
CREATE OR REPLACE VIEW MAIN_VIEW AS
SELECT id, 0 as new_rows FROM MAIN_TABLE;
View MAIN_VIEW created.
INSERT INTO main_view (id) values (100);
1 row inserted.
It is possible to insert into the view. Since there is a 1:1 relationship with the main_table this will insert a row in MAIN_TABLE
SELECT COUNT(*) FROM main_table;
COUNT(*)
----------
1
Now create the instead of trigger on MAIN_VIEW
CREATE OR REPLACE TRIGGER main_view_ioi
INSTEAD OF INSERT
ON main_view
FOR EACH ROW
BEGIN
FOR i IN 1 .. :NEW.NEW_ROWS LOOP
INSERT INTO main_table (id) VALUES (i);
END LOOP;
END;
/
Trigger MAIN_VIEW_IOI compiled
INSERT INTO main_view (id,new_rows) values (1,5);
1 row inserted.
Note that this will say "1 row inserted" because only 1 row was inserted in MAIN_VIEW. However, the instead of trigger created 5 rows in MAIN_TABLE.
SELECT * FROM main_table;
ID
----------
100
1
2
3
4
5

Related

Accessing old and new values without :OLD and :NEW in a trigger

As discussed here, I'm unable to use :OLD and :NEW on columns with collation other than USING_NLS_COMP. I'm trying to find a way around this but haven't been successful so far.
This is the original trigger:
CREATE OR REPLACE TRIGGER SYS$PERSONSSALUTATIONAU
AFTER UPDATE ON PERSONS
FOR EACH ROW
begin
State_00.Salutations_ToDelete(State_00.Salutations_ToDelete.Count + 1) := :old.SalutationTitle;
State_00.Salutations_ToInsert(State_00.Salutations_ToInsert.Count + 1) := :new.SalutationTitle;
end;
This is what I've tried:
CREATE OR REPLACE TRIGGER SYS$PERSONSSALUTATIONAU
FOR UPDATE ON Persons
COMPOUND TRIGGER
TYPE Persons_Record IS RECORD (
SalutationTitle NVARCHAR2(30)
);
TYPE Persons_Table IS TABLE OF Persons_Record INDEX BY PLS_INTEGER;
gOLD Persons_Table;
gNEW Persons_Table;
BEFORE EACH ROW IS BEGIN
SELECT SalutationTitle
BULK COLLECT INTO gOLD
FROM Persons
WHERE ID = :OLD.ID;
END BEFORE EACH ROW;
AFTER EACH ROW IS BEGIN
SELECT SalutationTitle
BULK COLLECT INTO gNEW
FROM Persons
WHERE ID = :NEW.ID;
END AFTER EACH ROW;
AFTER STATEMENT IS BEGIN
FOR i IN 1 .. gNEW.COUNT LOOP
State_00.Salutations_ToDelete(State_00.Salutations_ToDelete.Count + 1) := gOLD(i).SalutationTitle;
State_00.Salutations_ToInsert(State_00.Salutations_ToInsert.Count + 1) := gNEW(i).SalutationTitle;
END LOOP;
END AFTER STATEMENT;
END;
This results in error ORA-04091. I've also tried moving the select into the AFTER STATEMENT section which works, but there is no way to access the old values. If somebody has a solution for this it would be most appreciated.
EDIT:
I created a minimal reproducible example:
CREATE TABLE example_table (
id VARCHAR2(10),
name NVARCHAR2(100)
);
CREATE TABLE log_table (
id VARCHAR2(10),
new_name NVARCHAR2(100),
old_name NVARCHAR2(100)
);
CREATE OR REPLACE TRIGGER example_trigger
AFTER UPDATE ON example_table
FOR EACH ROW BEGIN
INSERT INTO log_table VALUES(:old.id, :new.name, :old.name);
END;
INSERT INTO example_table VALUES('01', 'Daniel');
-- this works as expected
UPDATE example_table SET name = ' John' WHERE id = '01';
SELECT * FROM log_table;
DROP TABLE example_table;
CREATE TABLE example_table (
id VARCHAR2(10),
-- this is the problematic part
name NVARCHAR2(100) COLLATE XCZECH_PUNCTUATION_CI
);
INSERT INTO example_table VALUES('01', 'Daniel');
-- here nothing is inserted into log_example, if you try to
-- recompile the trigger you'll get error PLS-00049
UPDATE example_table SET name = ' John' WHERE id = '01';
SELECT * FROM log_table;
DROP TABLE example_table;
DROP TABLE log_table;
DROP TRIGGER example_trigger;
In the discussion you reference a document concerning USING_NLS_COMP. That has nothing to do with the error you are getting. The error ORA-04091 is a reference to the table that fired the trigger (mutating). More to come on this. I am not saying you do not have USING_NLS_COMP issues, just that they are NOT causing the current error.
There are misconceptions shown in your trigger. Beginning with the name itself; you should avoid the prefix SYS. This prefix is used by Oracle for internal objects. While SYS prefix is not specifically prohibited at best it causes confusion. If this is actually created in the SYS schema then that in itself is a problem. Never use SYS schema for anything.
There is no reason to create a record type containing a single variable, then create a collection of that type, and finally define variables of the collection. Just create a collection to the variable directly, and define variables of the collection.
The bulk collect in the select statements is apparently misunderstood as used. I assume you want to collect all the new and old values in the collections. Bulk collect however will not do this. Each time bulk collect runs the collection used is cleared and repopulated. Result being the collection contains only the only the LAST population. Assuming id is unique the each collection would contain only 1 record. And now that brings us to the heart of the problem.
The error ORA-04091: <table name> is mutating, trigger/function may not see it results from attempting to SELECT from the table that fired the trigger; this is invalid. In this case the trigger fired due to a DML action on the persons table as a result you cannot select from persons in a row level trigger (stand alone or row level part of a compound trigger. But it is not needed. The pseudo rows :old and :new contain the complete image of the row. To get a value just reference the appropriate row and column name. Assign that to your collection.
Taking all into account we arrive at:
create or replace trigger personssalutation
for update
on persons
compound trigger
type persons_table is table of
persons.salutationtitle%type;
gold persons_table := persons_table();
gnew persons_table := persons_table();
before each row is
begin
gold.extend;
gold(gold.count) := :old.salutationtitle;
end before each row;
after each row is
begin
gnew.extend;
gold(gold.count) := :new.salutationtitle;
end after each row;
after statement is
begin
for i in 1 .. gnew.count loop
state_00.salutations_todelete(state_00.salutations_todelete.count + 1) := gold(i);
state_00.salutations_toinsert(state_00.salutations_toinsert.count + 1) := gnew(i);
end loop;
end after statement;
end personssalutation;
NOTE: Unfortunately you did not provide sample data, nor description of the functions in the AFTER STATEMENT section. Therefore the above is not tested.

Oracle trigger to increment value in another table after inserted in other

How to create trigger or procedure to add incrementally +1 in column VALUE in TABLE1 every single time when something is inserted in TABLE2 ?
Lets say that we start from VALUE.TABLE1 = 1 and I insert record in TABLE2. After that I should see 2 in VALUE column in TABLE1.
Then someone else is inserting record in TABLE2 and that procedure/trigger should add yet again +1 in VALUE and now it should be 3
etc.
DB is Oracle 11
Thanks.
Don't use table table1 and don't use a trigger.
Use a view instead:
create or replace view v_table1 as
select count(*) + 1 as value
from table2;
CREATE TRIGGER table2_trg
AFTER INSERT
ON table2
FOR EACH ROW
BEGIN
UPDATE table1 set value = value + 1;
END;
Or
CREATE or REPLACE TRIGGER TABLE2_trg
FOR INSERT
ON TABLE2
COMPOUND TRIGGER
cnt PLS_INTEGER := 0;
AFTER EACH ROW IS
BEGIN
cnt := cnt + 1;
END AFTER EACH ROW ;
AFTER STATEMENT IS
BEGIN
UPDATE table1 SET value = value + cnt;
END AFTER STATEMENT;
END;
I am not sure it is better or not and how it will work when some rows in table2 are dropped due to exceptions at the time of insertion.
It's better to use view if don't bother about deleted rows in table2.

Is before update trigger will fire if same values are updating into a row of column

I am using before update trigger for each row on table, say emp_table to update one column modifid_date before loading into table. If I am going to update the table with same/existing values of a row, then is this trigger going to fire or not?
condition in trigger:
:new.modifid_dt := sysdate;
Table Values before update: john (name),4867 (id),20-04-2016 (modifid_dt)
Table values now going to update: john (name),4867 (id)
Your trigger will be fired, no matter the values you are using; for example:
SQL> create table testTrigger ( a number)
2 /
Table created.
SQL> CREATE OR REPLACE TRIGGER before_update_trigger
2 before update on testTrigger
3 for each row
4 begin
5 dbms_output.put_line('Trigger fired!');
6 end;
7 /
Trigger created.
SQL> insert into testTrigger values (10);
1 row created.
SQL>
SQL>
SQL> update testTrigger set a = 10;
Trigger fired!
1 row updated.
SQL> update testTrigger set a = 11;
Trigger fired!
1 row updated.
SQL>
If you want avoid "false" firing you should write trigger like this:
create or replace trigger trigger1
before update on tst
for each row
begin
IF :new.t_key != :old.t_key AND ... THEN
dbms_output.put_line('Trigger fired!');
END IF;
end;
But beware of NULL values, of course.
New or existing values - no matter, anyway you'll perform an update so trigger will fire.

Oracle 'statement level' Trigger

I want to create a Statement level Trigger which means I want to insert only one record into table EMP_AUDIT when 1 or more rows are inserted into table EMP. For example: if I have 10 records inserted into EMP, then only 1 record should be inserted into EMP_AUDIT table.
There are no constraints on columns. (i.e. can be NULL)
I tried to use the following trigger but it gives me the Error(2,2): PL/SQL: SQL Statement ignored
Error(2,14): PL/SQL: ORA-00947: not enough values
CREATE OR REPLACE
TRIGGER TRIG_EMP AFTER INSERT ON EMP
BEGIN
INSERT INTO EMP_AUDIT
VALUES (TRANID,EMPNUM,SYSDATE);
END;
CREATE TABLE EMP
(TRANID NUMBER,
EMPNUM VARCHAR2(100),
EMPLOC VARCHAR2(100));
CREATE TABLE EMP_AUDIT
(EVENTID NUMBER,
EMPNUM VARCHAR2(100),
ENTRDATE DATE);
The statement-level trigger (which you have) cannot see the data that was inserted. After all, if there were 10 rows inserted, what values should the columns be for your audit table?
You need a row-level trigger for this to work, e.g.:
CREATE OR REPLACE
TRIGGER TRIG_EMP
AFTER INSERT ON EMP
FOR EACH ROW
BEGIN
INSERT INTO EMP_AUDIT
VALUES (:NEW.TRANID,:NEW.EMPNUM,:NEW.SYSDATE);
END;
Use this piece of code:
CREATE OR REPLACE TRIGGER
TRIG_EMP
AFTER INSERT ON EMP
FOR EACH ROW
BEGIN
INSERT INTO EMP_AUDIT
VALUES (:NEW.TRANID,:NEW.EMPNUM,:NEW.SYSDATE);
END;

Why does code throw java.sql.SQLException: ORA-01438?

I'm inserting data in table through this statement:
insert into CATEGORY_MASTER (
CAT_MAS_ID,
DESCRIPTION, ORG_ID, STATUS, MODIFY_EMPID, LANGUAGE_ID, LG_IP_MAC)
values (
( SELECT COALESCE(MAX(ct.cat_mas_id), 0)+1
FROM category_master ct),
'fff', 2, 'A', 52,1,'SYSTEM/127.0.0.1/NOTDEFINE')
The target table has this trigger:
create or replace trigger trg_aft_i_u_category_master
after insert OR UPDATE of cat_mas_id,status on category_master FOR EACH ROW
DECLARE
CURSOR CSTYPE IS
SELECT CST.SUB_TYPE_ID,CST.TYPE_ID,CST.ORG_ID,CST.STATUS
FROM COMPLAINT_SUB_TYPE CST
WHERE CST.ORG_ID=:NEW.ORG_ID AND CST.STATUS='A';
V_CSTYPE CSTYPE%ROWTYPE;
BEGIN
IF CSTYPE%ISOPEN THEN
CLOSE CSTYPE;
END IF;
OPEN CSTYPE;
LOOP
FETCH CSTYPE INTO V_CSTYPE;
EXIT WHEN CSTYPE%NOTFOUND;
if INSERTING then
/******** Suspect issue here *******/
INSERT INTO CATEGORY_DETAILS(
CAT_DTL_ID, CAT_MAS_ID, TYPE_ID ,SUB_TYPE_ID,
ORG_ID,MAP_STATUS,MODIFY_EMPID,LANGUAGE_ID,LG_IP_MAC)
VALUES (SEQ_CATEGORY_DETAILS.NEXTVAL,:NEW.CAT_MAS_ID,
V_CSTYPE.TYPE_ID,V_CSTYPE.SUB_TYPE_ID,:NEW.ORG_ID,'U',
:NEW.MODIFY_EMPID,:NEW.LANGUAGE_ID,:NEW.LG_IP_MAC);
/************************************/
end if;
if UPDATING then
if :new.status = 'I' then
UPDATE CATEGORY_DETAILS CD
SET CD.MAP_STATUS= 'U'
WHERE CD.CAT_MAS_ID=:NEW.CAT_MAS_ID AND CD.ORG_ID=:NEW.ORG_ID;
end if;
end if;
END LOOP;
CLOSE CSTYPE;
end trg_aft_i_u_category_master;
The explanantion for ORA-01438 is:
"value larger than specified precision allowed for this column"
So one of your tables (not necessarily MASTER_CATEGORY) has a number column defined with significant digits, and your code is trying to insert a number which is too large.
Given this table ...
SQL> create table t42 (col1 number(5,2));
Table created.
SQL>
... we can insert a value which fits the declaration:
SQL> insert into t42 values (123.45);
1 row created.
SQL>
... the database rounds up trailing decimals:
SQL> insert into t42 values (12.345);
1 row created.
SQL>
... and the database rejects the value when the leading element is too large:
SQL> insert into t42 values (1234.5);
insert into t42 values (1234.5)
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
SQL>
This now becomes an issue for you. You need to describe your tables to see which columns are defined as precise numbers, that is like NUMBER(3) or NUMBER(7,2). Then check the data you are using to estabish which numeric value is too big. Standard debugging techniques will help.

Resources