Whatever I do to the trigger always returns "0 rows inserted".
It is like it can't find the new values after inserting them.
After adding the exception it return no_data_found and i don't know why.
before insert or update of rent_date, return_date on rent
for each row
declare
pragma AUTONOMOUS_TRANSACTION;
v_rentDate date;
v_returnDate date;
begin
select rent_date
into v_rentDate
from rent
where rent_date = :new.rent_date;
select return_date
into v_returnDate
from rent
where return_date = :new.return_date;
if v_returnDate < v_rentDate then
raise_application_error(-20158, 'Return date must be after the rent date');
else
dbms_output.put_line('TEST');
end if;
exception when no_data_found then raise_application_error(-20157, 'No data found');
end;
/
insert into rent values (82,sysdate-5,101,sysdate,sysdate+5,100);
--0 rows inserted
It appears that you're doing it the wrong way. Here's why:
you are trying to select values from a table you're currently inserting into (or updating existing values)
Oracle complains that it can't do that because the table is mutating
in order to "fix" it, you used pragma autonomous_transaction which isolates trigger code from the main transaction
You shouldn't use that pragma for such a purpose. Lucky you, trigger can be rewritten in a simpler manner, the one that doesn't cause the mutating table error. As you want to compare rent_date and return_date, do it directly. Here's an example (see line #5):
SQL> create table rent
2 (id number,
3 rent_date date,
4 return_date date
5 );
Table created.
SQL> create or replace trigger trg_biu_rent
2 before insert or update on rent
3 for each row
4 begin
5 if :new.return_date < :new.rent_date then
6 raise_application_error (-20158, 'Return date must be after the rent date');
7 end if;
8 end;
9 /
Trigger created.
Testing:
SQL> -- This will fail
SQL> insert into rent (id, rent_date, return_date) values
2 (1, date '2019-05-25', date '2019-04-10');
insert into rent (id, rent_date, return_date) values
*
ERROR at line 1:
ORA-20158: Return date must be after the rent date
ORA-06512: at "SCOTT.TRG_BIU_RENT", line 3
ORA-04088: error during execution of trigger 'SCOTT.TRG_BIU_RENT'
SQL> -- This is OK
SQL> insert into rent (id, rent_date, return_date) values
2 (1, date '2019-03-28', date '2019-10-20');
1 row created.
SQL>
I have a table which have a field as nested table. I have a trigger on the first table, but it does not work and results on a :ORA-22903: MULTISET expression not allowed
CREATE OR REPLACE TYPE DIA_T as object
(dia varchar2(9),
hora varchar2(6));
CREATE OR REPLACE TYPE DIA_TAB IS TABLE OF DIA_T;
CREATE TABLE T_TALLER(
PK NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
...
DIAS DIA_TAB,
FEC_INI DATE NOT NULL,
FEC_FIN DATE NOT NULL,
...
CONSTRAINT CST_PRIMKEY_TALLER PRIMARY KEY (PK),
...
) NESTED TABLE DIAS STORE AS DIAS_TAB;
My trigger is:
CREATE OR REPLACE TRIGGER TRG_TALLER_AU_FEC_FIN AFTER UPDATE OF FEC_FIN ON T_TALLER FOR EACH ROW
BEGIN
FOR REC IN (SELECT T.DIA , T.HORA
FROM TABLE(:NEW.DIAS) T) LOOP
dbms_output.put_line(REC.DIA||' '||REC.HORA);
END LOOP;
END;
When I try something like that:
update t_taller set fec_fin = fec_fin + 20 where pk = 10;
I get the following error:
ORA-22903: MULTISET expression not allowed
ORA-06512: at "ESTAMPAS.TRG_TALLER_AU_FEC_FIN", line 3
ORA-04088: error during execution of trigger 'ESTAMPAS.TRG_TALLER_AU_FEC_FIN'
Can you help me to solve this problem?
Thanks in advance.
Regards,
UPDATE
The trigger I posted is a dummy, but the error i get for the real one is the same, my real trigger is this
CREATE OR REPLACE TRIGGER TRG_TALLER_AU_FEC_FIN AFTER UPDATE OF FEC_FIN ON T_TALLER FOR EACH ROW
BEGIN
IF :NEW.FEC_FIN >= :OLD.FEC_FIN THEN
Pkg_Utilidades.p_ins_taller_clase_grupo(:NEW.PK,(:OLD.FEC_FIN) + 1,:NEW.FEC_FIN,:NEW.DIAS,:NEW.AU_USU_INS);
ELSE
DELETE T_TALLER_CLASE
WHERE FK_TALLER = :NEW.PK
AND FEC_CLASE BETWEEN :NEW.FEC_FIN + 1 AND :OLD.FEC_FIN;
END IF;
END;
Something else to say, I have a "AFTER INSERT" Trigger, and it Works fine:
CREATE OR REPLACE TRIGGER TRG_TALLER_AI_CLASE AFTER INSERT ON T_TALLER FOR EACH ROW
BEGIN
DELETE T_TALLER_CLASE WHERE FK_TALLER = :NEW.PK;
Pkg_Utilidades.p_ins_taller_clase_grupo(:NEW.PK,:NEW.FEC_INI,:NEW.FEC_FIN,:NEW.DIAS,:NEW.AU_USU_INS);
END;
The procedure is:
PROCEDURE p_ins_taller_clase_grupo (p_taller NUMBER,
p_fec_ini DATE,
p_fec_fin DATE,
p_dias DIA_TAB,
p_user VARCHAR2) IS
p_output VARCHAR2(100);
v_dia NUMBER;
BEGIN
FOR REC IN (SELECT p_fec_ini + LEVEL - 1 FECHA,
DECODE(TO_CHAR(p_fec_ini + LEVEL - 1 , 'DAY'),'MONDAY ',1,'TUESDAY ',2,'WEDNESDAY',3,'THURSDAY ',4,'FRIDAY ',5,'SATURDAY ',6,7) DIA
FROM DUAL
CONNECT BY LEVEL <= p_fec_fin - p_fec_ini + 1) LOOP
BEGIN
SELECT D INTO v_dia
FROM (
SELECT decode(upper(T.dia),'LUNES',1,'MARTES',2,'MIERCOLES',3,'MIÉRCOLES',3,'JUEVES',4,'VIERNES',5,'SABADO',6,'SÁBADO',6,7) D
FROM TABLE(p_dias) T
)
WHERE D = REC.DIA;
P_INS_TALLER_CLASE (p_taller,REC.FECHA,Pkg_conf.CST_HORA,p_user,p_output);
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
END LOOP;
END p_ins_taller_clase_grupo;
The insert Works fine:
SQL> insert into t_taller (FK_profesor,fk_danza,fk_local,fk_periodicidad,fec_ini,fec_fin,dias,AU_USU_INS) values (1,1,1,1,to_date('05/01/2019','dd/mm/yyyy'),to_date('27/01/2019','dd/mm/yyyy'),dia_tab(dia_t('SABADO','10:30'),dia_t('DOMINGO','10:30')),'EP_PL01');
1 row created.
SQL> commit;
Commit complete.
SQL> update t_taller set fec_fin = fec_fin + 20 where pk = 24;
update t_taller set fec_fin = fec_fin + 20 where pk = 24
*
ERROR at line 1:
ORA-22903: MULTISET expression not allowed
ORA-06512: at "ESTAMPAS.PKG_UTILIDADES", line 451
ORA-06512: at "ESTAMPAS.TRG_TALLER_AU_FEC_FIN", line 5
ORA-04088: error during execution of trigger 'ESTAMPAS.TRG_TALLER_AU_FEC_FIN'
The line 451 of the package, is inside the procedure, exactly here:
SELECT D INTO v_dia
FROM (
SELECT decode(upper(T.dia),'LUNES',1,'MARTES',2,'MIERCOLES',3,'MIÉRCOLES',3,'JUEVES',4,'VIERNES',5,'SABADO',6,'SÁBADO',6,7) D
FROM TABLE(p_dias) T
)
WHERE D = REC.DIA;
Sorry for not posted all the details from the begining, i wanted to summayrize and show just the error.
Regards
TABLE expression works if you are running a select query on the table itself.
Something like
SELECT T.DIA , T.HORA
FROM T_TALLER ,TABLE(:NEW.DIAS) T
But, you are not allowed to select from the Trigger owner as it leads to "table is mutating" error( ORA-04091 ).
You may instead loop through the nested table column using a simple for loop.
CREATE OR REPLACE TRIGGER trg_taller_au_fec_fin AFTER
UPDATE OF fec_fin ON t_taller
FOR EACH ROW
BEGIN
FOR i in 1 .. :new.dias.count
LOOP
dbms_output.put_line(:new.dias(i).dia || ' ' || :new.dias(i).hora);
END LOOP;
END;
/
By the way, as #XING mentioned, there's no use of dbms_output in a Trigger. You should rather consider logging them into a table.
Demo
As mentioned in my comments Triggers are used for handling of events on the table to . Although your posted script does not make much sense to me , however am giving the solution.
Also you simply cannot Select records from a Nested table. You need to use table operator to do so. Also in a trigger if you do Select on same table, you might land up is mutating, trigger/function. See below demo:
CREATE OR REPLACE TYPE dia_t AS OBJECT (
dia VARCHAR2(9),
hora VARCHAR2(6)
);
CREATE OR REPLACE TYPE DIA_TAB IS TABLE OF DIA_T;
CREATE TABLE t_taller (
pk NUMBER ,
dias dia_tab,
fec_ini DATE NOT NULL,
fec_fin DATE NOT NULL,
CONSTRAINT cst_primkey_taller PRIMARY KEY ( pk )
)
NESTED TABLE dias STORE AS dias_tab;
INSERT INTO t_taller VALUES (
1,
dia_tab(dia_t(
'A',
'B')
),
TO_DATE('10-Dec-2018','DD-Mon-YYYY'),
TO_DATE('10-Dec-2018','DD-Mon-YYYY')
);
CREATE OR REPLACE TRIGGER trg_taller_au_fec_fin
AFTER UPDATE OF fec_fin ON t_taller
FOR EACH ROW
BEGIN
FOR rec IN ( SELECT t.dia,
t.hora
FROM t_taller e,TABLE (e.dias ) t) --This is how you select records from nested table
LOOP
dbms_output.put_line(rec.dia || ' ' || rec.hora);
END LOOP;
END;
Output:
Error starting at line : 40 in command -
update t_taller set fec_fin = fec_fin + 20 where pk = 1
Error report -
ORA-04091: table SYSTM.T_TALLER is mutating, trigger/function may not see it
ORA-06512: at "SYSTM.TRG_TALLER_AU_FEC_FIN", line 2
ORA-04088: error during execution of trigger 'SYSTM.TRG_TALLER_AU_FEC_FIN'
So again you need to revisit your requirement.
Yes, I ask numerous questions because I learn more here than from the books. I've created a simple code block that produces desired output but it seems to simple for the current learning block i'm in. Would this code work for anyone that attempts to update the iddonor for a donor table if the id already exists? Haven't learned procedures or functions yet but can guess that would be a more sensible method. Does what i have thus far satisfy and exception handler if a condition arises or should I add more in the declaration? Appreciate the suggestions and learning points if provided.
My Code:
BEGIN
INSERT INTO dd_donor (iddonor)
VALUES (305)
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('ID already Exists');
END;
DUP_VAL_ON_INDEX exception is raised when you try to store duplicate values in a column that is supported by a unique index.
Let's see a simple example. I have created a table with single column and made it the primary key, which will be supported by an implicit unique index.
Setup
SQL> CREATE TABLE t(ID NUMBER);
Table created.
SQL> ALTER TABLE t ADD CONSTRAINT t_uk PRIMARY KEY(ID);
Table altered.
SQL> INSERT INTO t(ID) VALUES(1);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM t;
ID
----------
1
SQL> BEGIN
2 INSERT INTO t(ID) VALUES(1);
3 END;
4 /
BEGIN
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.T_UK) violated
ORA-06512: at line 2
So, the unique constraint is violated. Let's see how to capture DUP_VAL_ON_INDEX exception:
Test case
SQL> SET serveroutput ON
SQL> BEGIN
2 INSERT INTO t(ID) VALUES(1);
3 EXCEPTION
4 WHEN DUP_VAL_ON_INDEX THEN
5 DBMS_OUTPUT.PUT_LINE('Duplicate value on index');
6 END;
7 /
Duplicate value on index
PL/SQL procedure successfully completed.
SQL>
By the way, the DBMS_OUTPUT was only for demo purpose, ideally you wouldn't have it in your production code.
I have some problem executing the trigger below:
CREATE OR REPLACE TRIGGER AFTERINSERTCREATEBILL
AFTER INSERT
ON READING
FOR EACH ROW
DECLARE
varReadNo Int;
varMeterID Int;
varCustID Varchar(10);
BEGIN
SELECT SeqReadNo.CurrVal INTO varReadNo FROM DUAL;
Select MeterID INTO varMeterID
From Reading
Where ReadNo = varReadNo;
Select CustID INTO varCustID
From Address A
Join Meter M
on A.postCode = M.postCode
Where M.MeterID = varMeterID;
INSERT INTO BILL VALUES
(SEQBILLNO.NEXTVAL, SYSDATE, 'UNPAID' , 100 , varCustID , SEQREADNO.CURRVAL);
END;
Error Message:
*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.
Does it mean that I am not suppose to retrieve any details from table Reading?
I believe that the issue occur as I retrieving data from Table Reading while it is inserting the value:
SELECT SeqReadNo.CurrVal INTO varReadNo FROM DUAL;
Select MeterID INTO varMeterID
From Reading
Where ReadNo = varReadNo;
It's there anyway to resolve this? Or is there a better method to do this? I need to insert a new row into table BILL after entering the table Reading where the ReadNo need to reference to the ReadNo I just insert.
You cannot retrieve records from the same table in a row trigger. You can access values from actual record using :new and :old (is this your case?). The trigger could then be rewritten to
CREATE OR REPLACE TRIGGER AFTERINSERTCREATEBILL
AFTER INSERT
ON READING
FOR EACH ROW
DECLARE
varCustID Varchar(10);
BEGIN
Select CustID INTO varCustID
From Address A
Join Meter M
on A.postCode = M.postCode
Where M.MeterID = :new.MeterID;
INSERT INTO BILL VALUES
(SEQBILLNO.NEXTVAL, SYSDATE, 'UNPAID' , 100 , varCustID , SEQREADNO.CURRVAL);
END;
If you need to query other record from READING table you have to use a combination of statement triggers, row trigger and a PLSQL collection. Good example of this is on AskTom.oracle.com
Make sure that you have the necessary permissions on all the tables and access to the sequences you're using in the insert.
I haven't done Oracle in awhile, but you can also try querying dba_errors (or all_errors) in order to try and get more information on why your SP isn't compiling.
Something to the tune of:
SELECT * FROM dba_errors WHERE owner = 'THEOWNER_OF_YOUR_SP';
Add exception handling in your trigger and see what is happening, by doing it would be easy for you to track the exceptions.
CREATE OR REPLACE TRIGGER AFTERINSERTCREATEBILL
AFTER INSERT
ON READING
FOR EACH ROW
DECLARE
varCustID Varchar(10);
BEGIN
-- your code
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQLERRM(-20299)));
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQLERRM(-20298)));
END;
we can combined insert and select statement
CREATE OR REPLACE TRIGGER AFTERINSERTCREATEBILL
AFTER INSERT
ON READING
FOR EACH ROW
DECLARE
varCustID Varchar(10);
BEGIN
insert into bill
values
select SEQBILLNO.NEXTVAL,
SYSDATE,
'UNPAID' ,
100 ,
CustID,SEQREADNO.CURRVAL
From Address A
Join Meter M
on A.postCode = M.postCode
Where M.MeterID = :new.MeterID;
END;
try the above code.
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.