Triggers in sql: Mutating Trigger issue - oracle

I want to insert rows into another table called excercise_scores when a row is inserted in attempts table. There are multiple tries for an excercise and based on the scoring method(which can be latest score, average of all attempts or maximum score from all attempts), I have to insert score in the excercise_scores table.
So when Inserting scores into excercise_score, I also have to check what is the grading method for the excercise, get the score from attempts table based on the method and then finally insert into excercise_scores table.
For that I have created a SQL trigger as follows
CREATE OR REPLACE TRIGGER scores_trigger AFTER
INSERT
ON attempts FOR EACH row DECLARE P1 VARCHAR2(50);
SCORES FLOAT;
v_exists VARCHAR2(1) := 'F';
BEGIN
BEGIN
SELECT
'T'
INTO
v_exists
FROM
excercise_scores
WHERE
user_id = :NEW.USER_ID
AND EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID;
EXCEPTION
WHEN no_data_found THEN
NULL;
END;
SELECT
SELECTION_METHOD
INTO
P1
FROM
EXCERCISES
WHERE
EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID;
IF (P1 = 'Latest') THEN
SCORES := :NEW.TOTAL_POINTS;
ELSE
IF (P1 = 'best') THEN
SELECT
MAX(TOTAL_POINTS)
INTO
SCORES
FROM
ATTEMPTS
WHERE
EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID
AND USER_ID = :NEW.USER_ID;
ELSE
SELECT
AVG(TOTAL_POINTS)
INTO
SCORES
FROM
ATTEMPTS
WHERE
EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID
AND USER_ID = :NEW.USER_ID;
END IF;
END IF;
IF v_exists = 'T' THEN
UPDATE
EXCERCISE_SCORES
SET
TOTAL_POINTS = SCORES
WHERE
user_id = :NEW.USER_ID
AND EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID;
ELSE
INSERT
INTO
EXCERCISE_SCORES VALUES
(
:NEW.EXCERCISE_ID,
:NEW.COURSE_ID,
:NEW.COURSE_ID,
SCORES
) ;
END IF;
END;
But when I try to execute this trigger, it gives me the following error:
Error starting at line 47 in command:
insert into attempts values(21,0,'vshesha',3,'CSC540',TO_DATE('20141014', 'YYYYMMDD'),12,6)
Error report:
SQL Error: ORA-04091: table VSHESHA.ATTEMPTS is mutating, trigger/function may not see it
ORA-06512: at "VSHESHA.SCORES_TRIGGER", line 18
ORA-04088: error during execution of trigger 'VSHESHA.SCORES_TRIGGER'
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.
I couldn't figure out why it is giving this error as I am running the trigger after insert. Generally mutating table issue should not come in after insert triggers.
Can anyone please help me with this. I am suck with this for a long time now.

The error message is pretty self-explaining, and the SQL statement that's causing it is:
SELECT
AVG(TOTAL_POINTS)
INTO
SCORES
FROM
ATTEMPTS
WHERE
EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID
AND USER_ID = :NEW.USER_ID;
You're selecting from ATTEMPTS, and that's the table that's currently changing. There's no simple solution for it - the most sensible approach is to use move your business logic out of the trigger and into your application code (see AskTom about mutating table errors ).
BTW: Your statement that "Generally mutating table issue should not come in after insert triggers." is plain wrong. See database journal article for a longer explanation (in short: you will always run into mutating table errors with multi-row inserts, and in almost every case run into errors with single-row inserts).

In Oralce you can use COMPOUND TRIGGER. In your case it would be similar to this one. I did not copy all your operations to this example, but you should get the principle.
However, as the other replies already mentionied. The preferred way should be to move all this logic into a PL/SQL procedure.
CREATE OR REPLACE TRIGGER scores_trigger
FOR INSERT ON attempts
COMPOUND TRIGGER
TYPE RowTableType IS TABLE OF ROWID;
RowTable RowTableType;
aRow attempts%ROWTYPE;
SCORES FLOAT;
v_exists VARCHAR2(1) := 'F';
BEFORE STATEMENT IS
BEGIN
RowTable := RowTableType();
END BEFORE STATEMENT;
AFTER EACH ROW IS
BEGIN
RowTable.EXTEND;
RowTable(RowTable.LAST) := :NEW.ROWID;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR i IN RowTable.FIRST..RowTable.LAST LOOP
SELECT *
INTO aRow
FROM attempts
WHERE ROWID = RowTable(i);
SELECT SELECTION_METHOD
INTO P1
FROM EXCERCISES
WHERE EXCERCISE_ID = aRow.EXCERCISE_ID
AND COURSE_ID = aRow.COURSE_ID;
IF P1 = 'Latest' THEN
SCORES := :NEW.TOTAL_POINTS;
ELSE
IF (P1 = 'best') THEN
SELECT MAX(TOTAL_POINTS)
INTO SCORES
FROM ATTEMPTS
WHERE EXCERCISE_ID = aRow.EXCERCISE_ID
AND COURSE_ID = aRow.COURSE_ID
AND USER_ID = aRow.USER_ID;
ELSE
SELECT AVG(TOTAL_POINTS)
INTO SCORES
FROM ATTEMPTS
WHERE EXCERCISE_ID = aRow.EXCERCISE_ID
AND COURSE_ID = aRow.COURSE_ID
AND USER_ID = aRow.USER_ID;
END IF;
END IF;
END LOOP;
END AFTER STATEMENT;
END scores_trigger;
An example for an PL/SQL procedure would be this one:
CREATE OR REPLACE PROCEDURE insert_score(V_EXCERCISE IN SCORES.EXCERCISE_ID%TYPE, V_COURSE IN SCORES.COURSE_ID%TYPE, V_USER IN USER_ID%TYPE) IS
BEGIN
INSERT INTO insert_score (EXCERCISE_ID, COURSE_ID, USER_ID) VALUES (V_EXCERCISE, V_COURSE, V_USER);
BEGIN
SELECT 'T'
INTO v_exists
FROM excercise_scores
WHERE user_id = :NEW.USER_ID
AND EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN;
END;
... other stuff
END insert_score;
For the insert you have to call this procedure instead of doing the insert directly.

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 Check Values On Insert & Update

I am trying to check a value-gap crossed with each other or not. Let me explain with a quick scenario.
Rec 1 : Company AA : Distance Gap -> 100 - 200
Rec 2 : Company AA : Distance Gap -> 200 - 300 VALID
Rec 3 : Company AA : Distance Gap -> 250 - 450 INVALID
Rec 4 : Company JL : Distance Gap -> 250 - 450 VALID
Rec 3 Invalid because it is between REC 2's distance values.
Rec 4 Valid because company is different
Thus I wrote a trigger to prevent it.
create or replace trigger ab_redemption_billing_mpr
after insert or update on redemption_billing_mpr
for each row
declare
v_is_distance_range_valid boolean;
begin
v_is_distance_range_valid := p_redemption_billing.is_distance_range_valid(:new.id,
:new.redemption_billing_company,
:new.distance_min,
:new.distance_max,
'redemption_billing_mpr');
if not v_is_distance_range_valid then
raise_application_error(-20001, 'This is a custom error');
end if;
end ab_redemption_billing_mpr;
FUNCTION:
function is_distance_range_valid(p_id in number,
p_company in varchar2,
p_distance_min in number,
p_distance_max in number,
p_table_name in varchar2) return boolean is
d_record_number number;
begin
execute immediate 'select count(*) from ' || p_table_name || ' r
where r.redemption_billing_company = :1
and (:2 between r.distance_min and r.distance_max or :3 between r.distance_min and r.distance_max)
and r.id = nvl(:4, id)'
into d_record_number
using p_company, p_distance_min, p_distance_max, p_id;
if (d_record_number > 0) then
return false;
else
return true;
end if;
end;
is_distance_range_valid() works just as I expected. If it returns false, that means range check is not valid and don't insert or update.
When I create a scenario to catch this exception, oracle gives
ORA-04091: table name is mutating, trigger/function may not see it
and it points select count(*) line when I click debug button.
I don't know why I am getting this error. Thanks in advance.
Just check whether the below approach resolves your issue
Create a log table with columns required for finding the is_distance_range_valid validation and In your trigger insert into this log tab.The trigger on main table has to be a before insert or update trigger
Create a trigger on this log table and do the validation in the log table trigger and raise error.The trigger on log table has to be an after insert or update trigger.
Also this will only work if the row is already existing in the main table and it is not part of the current insert or update. If validation of the current insert or update is required then we need to use the :new.column_name to do the validation
Test:-
create table t_trg( n number);
create table t_trg_log( n number);
create or replace trigger trg_t
before insert or update on t_trg
for each row
declare
l_cnt number;
begin
insert into t_trg_log values(:new.n);
end trg_t;
create or replace trigger trg_t_log
after insert or update on t_trg_log
for each row
declare
l_cnt number;
begin
select count(1) into l_cnt from t_trg
where n=:new.n;
if l_cnt > 0 then
raise_application_error(-20001, 'This is a custom error');
end if;
end trg_t_log;
The first time I insert it doesn't throw error
insert into t_trg values(7);
1 row inserted.
The second time I execute, I get the custom error
insert into t_trg values(7);
Error report -
ORA-20001: This is a custom error
ORA-06512: at "TRG_T_LOG", line 7
ORA-04088: error during execution of trigger 'TRG_T_LOG'
ORA-06512: at "TRG_T", line 4
ORA-04088: error during execution of trigger 'TRG_T'
Update:-Using compound trigger the error is thrown in the first time the insert is done since it also takes into account the current row that we are updating or inserting while doing the SELECT
First I want to thanks to psaraj12 for his effort.
I found solution. Oracle forces us to not use select query before each row.
Oracle trigger error ORA-04091
So I wrote this and it works.
create or replace trigger ab_redemption_billing_mpr
for insert or update on redemption_billing_mpr
compound trigger
v_is_distance_range_valid boolean;
id redemption_billing_mpr.id%type;
redemption_billing_company redemption_billing_mpr.redemption_billing_company%type;
distance_min redemption_billing_mpr.distance_min%type;
distance_max redemption_billing_mpr.distance_max%type;
after each row is
begin
id := :new.id;
redemption_billing_company := :new.redemption_billing_company;
distance_min := :new.distance_min;
distance_max := :new.distance_max;
end after each row;
after statement is
begin
v_is_distance_range_valid := p_redemption_billing.is_distance_range_valid(id,
redemption_billing_company,
distance_min,
distance_max,
'redemption_billing_mpr');
if not v_is_distance_range_valid then
raise_application_error(-20001, 'This is a custom error');
end if;
end after statement;
end ab_redemption_billing_mpr;
We must use compound trigger to deal with it.

Trying to create a trigger to check if there's more than 1 president in my database

I'm trying to find if there's more than 1 president in my database with a trigger and if yes, raise an error, I'm using hr, the table employees and I have to use the job_id to find it. Here's what my code looks like. Thanks!
CREATE OR REPLACE TRIGGER check_pres
BEFORE INSERT OR DELETE OR UPDATE ON employees
FOR EACH ROW
BEGIN
IF ((employees.job_id = 'AD_PRES') > 1)
THEN RAISE_APPLICATION_ERROR(-12345, 'More than one President in database.');
END IF;
END;
You should use Statement Level Trigger instead of Row Level Trigger by removing FOR EACH ROW expression
CREATE OR REPLACE TRIGGER check_pres
BEFORE INSERT OR DELETE OR UPDATE ON employees
DECLARE
v_cnt int;
BEGIN
SELECT COUNT(*) INTO v_cnt FROM employees WHERE job_id = 'AD_PRES';
IF ( v_cnt > 1 ) THEN
RAISE_APPLICATION_ERROR(-20345, 'More than one President in database.');
END IF;
END;
otherwise you'd get mutating error while getting the count value. Btw, the first argument value for RAISE_APPLICATION_ERROR should be between -20999 and -20000

Use fields from another table as max-number-rows-with-type constrain in oracle

I have two tables:
CREATE TABLE users (
user_id INT(7) NOT NULL,
restricted_type VARCHAR(64) NOT NULL
)
CREATE TABLE type_restrictions (
name VARCHAR(64) NOT NULL,
restriction INT NOT NULL
)
I want to check on insert, that there are no more than restriction users with restricted_type = type_restriction.name.
At this point I'm inserting data with this query:
INSERT INTO users (user_id, restricted_type) SELECT <id>, <type> FROM DUAL
WHERE NOT EXISTS (
SELECT 1
FROM type_restrictions T
WHERE T.name = <type> AND T.restriction < (
SELECT COUNT(*)
FROM users U
WHERE U.user_id = <id> AND U.restricted_type = <type>)
)
But with two or more parallel queries it is possible to end up with more users with restricted_type than actual restriction for this type.
Is there any way to make such constraint work? (Also, I always insert only one row per query, if it helps)
You cannot use select ... in constraint. You cannot select from table which you are inserting into in normal trigger. What you can do? Materialized view (probably, I am not sure) or compound trigger. Here is my (working) try:
create or replace trigger trg_users_restrict
for insert on users compound trigger
type tt is table of number index by varchar2(5);
vt tt;
i varchar2(5);
v_max int;
before statement is
begin
for r in (select restricted_type, count(1) cnt from users group by restricted_type)
loop
vt(r.restricted_type) := r.cnt;
end loop;
end before statement;
after each row is
begin
begin
vt(:new.restricted_type) := vt(:new.restricted_type) + 1;
exception when no_data_found then
vt(:new.restricted_type) := 1;
end;
end after each row;
after statement is
begin
i := vt.first;
while i is not null loop
select nvl(max(restriction), 0) into v_max
from type_restrictions where name = i;
if vt(i) > v_max then
raise_application_error( -20001,
'maximum number exceeded for restriction type ' || i );
end if;
i := vt.next(i);
end loop;
end after statement;
end trg_users_restrict;
In before statement I grouped data from users table into collection. In after each row I increased proper values in collection for newly inserted row(s). In after statement I check if data in collection exceeds allowed ranges in table type_restrictions.
When two sessions insert concurent data then this which commits last causes exception.

Oracle Compound Trigger Mutation Table

I'm trying to create a compound trigger to avoid the mutation problem.
I've a table and a python's procedure that perfoms a transaction insert. The table has n fields.
What I´m trying to do is when a value of one of those fields is negative, then do not perform the operation , and insert the value from the previous record of the field (prior to insert) of the table. Another concern is that one of the fields is and id, to distinguish between sites.
For no, this is the code I've, Considering only one field (KWHGEN):
CREATE OR REPLACE TRIGGER "CIRCU3".D_measures_TP_test
--FOR INSERT OR UPDATE ON T_MEASURES_TP_NEW
FOR INSERT ON T_MEASURES_TP_NEW
COMPOUND TRIGGER
VAL_KWHGEN NUMBER(21,2);
VAL_autoin NUMBER (19,0);
AFTER EACH ROW IS
BEGIN
SELECT autoin, KWHGEN INTO VAL_ID_MED, VAL_KWHGEN FROM
(SELECT *
FROM T_measures_TP_NEW WHERE ID_site = :NEW.ID_site
ORDER BY TIMESTAMP DESC)
WHERE ROWNUM = 1;
IF :NEW.KWHGEN <0
THEN UPDATE T_MEASURES_TP_NEW SET KWHGEN = VAL_KWHGEN WHERE autoin = VAL_autoin;
END IF;
END AFTER EACH ROW;
END D_MEASURES_TP_test;
But the mutation error is following me ;-)
You have created trigger on T_MEASURES_TP_NEW and then updating same table T_MEASURES_TP_NEW within trigger. This will again call your trigger.
If the first select in trigger again returns negative value in VAL_KWHGEN then mutating error will follow you.
You defined only an AFTER EACH block, nothing else. This is the same as creating a row-level trigger (i.e. using FOR EACH ROW)
It must be like this (not tested):
CREATE OR REPLACE TRIGGER "CIRCU3".D_measures_TP_test
FOR INSERT ON T_MEASURES_TP_NEW
COMPOUND TRIGGER
VAL_KWHGEN NUMBER(21,2);
VAL_autoin NUMBER (19,0);
TYPE RowIdTableType IS TABLE OF ROWID;
TYPE KWHGENTableType IS TABLE OF T_MEASURES_TP_NEW.KWHGEN%TYPE;
RowIdTable RowIdTableType;
KWHGENTable KWHGENTableType;
BEFORE STATEMENT IS
BEGIN
RowIdTable := RowIdTable();
KWHGENTable := KWHGENTableType();
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
RowIdTable.EXTEND;
RowIdTable(RowIdTable.LAST) := :NEW.ROWID;
KWHGENTable.EXTEND;
KWHGENTable(RowIdTable.LAST) := :NEW.KWHGEN;
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR i IN RowIdTable.FIRST..RowIdTable.LAST LOOP
SELECT
DISTINCT MIN(autoin) OVER (ORDER BY TIMESTAMP DESC),
DISTINCT MIN(KWHGEN) OVER (ORDER BY TIMESTAMP DESC)
INTO VAL_ID_MED, VAL_KWHGEN
FROM T_measures_TP_NEW
WHERE ROWID = RowIdTable(i);
IF KWHGENTable(i) < 0
THEN UPDATE T_MEASURES_TP_NEW
SET KWHGEN = VAL_KWHGEN
WHERE autoin = VAL_autoin;
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/
OK, I do have a solution:
1.- Create a package where record the new insert data (BEFORE)
create or replace PACKAGE PCK_MEDIDAS_TP AS
TYPE DATOS_MEDIDAS_TP IS RECORD(
v_id_sede NUMBER (10,0),
v_id_med NUMBER (10,0),
v_kwhGEN NUMBER (21,2),
v_timestamp TIMESTAMP
);
type T_MEDTP is table of DATOS_MEDIDAS_TP index by binary_integer;
tabla_medidas_tp T_MEDTP;
END PCK_MEDIDAS_TP;
2.- Create a procedure each row (BEFORE) to read the new insert data and then record them into de package's table.
create or replace TRIGGER "CIRCU3".D_MEDIDAS_TP_test
BEFORE INSERT ON T_MEDIDAS_TP_NEW
FOR EACH ROW
DECLARE
Indice binary_integer;
BEGIN
--AUTOINCREMENTAL DEL CAMPO ID_MEDIDAS
SELECT T_MEDIDAS_TP_NEW_SEQ.NEXTVAL INTO :NEW.id_MEDIDAS_OLD FROM DUAL;
Indice:= PCK_MEDIDAS_TP.tabla_medidas_tp.COUNT+1;
PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_sede := :NEW.ID_SEDE;
PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_med := :NEW.ID_MEDIDAS;
PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_kwhGEN := :NEW.KWHGEN;
PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_timestamp := :NEW.TIMESTAMP;
IF :NEW.KWHGEN <0 THEN
DBMS_OUTPUT.put_line('first trigger:' ||:NEW.ID_MEDIDAS||','||:NEW.ID_SEDE||','||:NEW.TIMESTAMP);
-- INSERT INTO TEST_TRIGGER VALUES ('100', :NEW.KWHGEN, SYSDATE);
--ELSE DBMS_OUTPUT.PUT_LINE('¿?');
END IF;
END;
3.- Create a statement procedure (AFTER) where you can check your condition, in my case if kwhgen <0. If is true, I'll read the previous record in the original tbale and update the insert record with taht value.
create or replace TRIGGER D_MEDIDAS_TP_TEST_STATEMENT
AFTER INSERT ON T_MEDIDAS_TP_NEW
DECLARE
Indice binary_integer;
s_id_sede NUMBER (10,0);
s_id_med NUMBER (10,0);
s_kwhGEN NUMBER (21,2);
s_timestamp TIMESTAMP;
BEGIN
FOR Indice in 1..PCK_MEDIDAS_TP.tabla_medidas_tp.count LOOP
DBMS_OUTPUT.put_line('second trigger: kwhgen: '||PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_kwhGEN||', id_sede: '||PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_sede);
IF PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_kwhGEN <0 THEN
DBMS_OUTPUT.put_line('second trigger: v_kwhGEN is negative');
SELECT prev_KWHGEN INTO s_kwhgen
from(
SELECT LEAD (KWHGEN,1) over (ORDER BY id_medidas desc) as prev_KWHGEN
FROM T_MEDIDAS_TP_NEW WHERE ID_SEDE = PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_sede
ORDER BY id_medidas DESC) where rownum =1;
INSERT INTO TEST_TRIGGER VALUES ('100', '5555', SYSDATE);
DBMS_OUTPUT.put_line('second trigger. KWHGEN: '||s_kwhGEN);
DBMS_OUTPUT.put_line('UPDATE');
UPDATE T_MEDIDAS_TP_NEW SET KWHGEN = S_KWHGEN WHERE ID_MEDIDAS = PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_med;
else DBMS_OUTPUT.put_line('¿?');
END IF;
END LOOP;
PCK_MEDIDAS_TP.tabla_medidas_tp.delete; -- vaciamos la tabla
END;

Resources