I've been searching for a while how to resolve this issue and couldn't find anything helpful.
Why this trigger is created with compilation errors?
The warning im getting
Warning: Trigger created with compilation errors.
CREATE OR REPLACE TRIGGER peope_in_dept
AFTER INSERT OR DELETE OR UPDATE on emp
FOR EACH ROW
BEGIN
VARIABLE v NUMBER;
EXECUTE :v := count_people_in_dept(:new.deptno)
IF :v <= 0 or :v >= 10 THEN
raise_application_error(-20000,'Nieodpowiednia liczba pracownikow');
END IF;
END;
/
Syntax errors. Should be
SQL> CREATE OR REPLACE TRIGGER peope_in_dept
2 AFTER INSERT OR DELETE OR UPDATE
3 ON emp
4 FOR EACH ROW
5 DECLARE
6 v NUMBER;
7 BEGIN
8 v := count_people_in_dept (:new.deptno);
9
10 IF v <= 0
11 OR v >= 10
12 THEN
13 raise_application_error (-20000, 'Nieodpowiednia liczba pracownikow');
14 END IF;
15 END;
16 /
Trigger created.
SQL>
However, I suspect it won't work if count_people_in_dept function counts employees in emp table. Why? Because table is mutating:
SQL> UPDATE emp SET comm = 1 WHERE deptno = 10;
UPDATE emp SET comm = 1 WHERE deptno = 10
*
ERROR at line 1:
ORA-04091: table SCOTT.EMP is mutating, trigger/function may not see it
ORA-06512: at "SCOTT.COUNT_PEOPLE_IN_DEPT", line 6
ORA-06512: at "SCOTT.PEOPE_IN_DEPT", line 4
ORA-04088: error during execution of trigger 'SCOTT.PEOPE_IN_DEPT'
SQL>
Now, it depends on what you really are doing in that function. One way out is to set the function to be an autonomous transaction; another one is to use compound trigger; the third option is to use custom type and a package. Or, modify the process altogether (i.e. avoid the trigger).
Why this trigger is created with compilation errors?
Because you are trying to create a SQL/Plus global bind variable inside a PL/SQL procedure and then using EXECUTE rather than just calling the function (and you missed a ; statement terminator).
The correct syntax would be:
FOR EACH ROW
DECLARE
v NUMBER;
BEGIN
v := count_people_in_dept(:new.deptno);
IF v <= 0 or v >= 10 THEN
Then it would compile. However, it still won't work as the table would be mutating as the trigger is running.
Instead, you can use a statement trigger rather than a row trigger:
CREATE OR REPLACE TRIGGER people_in_dept
AFTER INSERT OR DELETE OR UPDATE on emp
DECLARE
v_count PLS_INTEGER;
BEGIN
SELECT 1
INTO v_count
FROM dept d
LEFT OUTER JOIN emp e
ON (e.deptno = d.deptno)
GROUP BY d.deptno
HAVING COUNT(e.deptno) NOT BETWEEN 1 AND 9
FETCH FIRST ROW ONLY;
raise_application_error(-20000,'Nieodpowiednia liczba pracownikow');
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Do nothing.
NULL;
END;
/
db<>fiddle here
You could also write it as a compound trigger that aggregates the changes in deptno and then only checks the changed values using your function; however, that is a lot more complicated.
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
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.
Hi im setting up some triggers and i cant get past this error
SET SERVEROUTPUT ON
CREATE OR REPLACE TRIGGER trigger2
BEFORE INSERT ON new_donation
FOR EACH ROW
--WHEN (new.contamt <= 10)
DECLARE
v_idno VARCHAR2(5);
v_driveno VARCHAR2(3);
v_contdate DATE;
v_contamt NUMBER(6,2);
BEGIN
SELECT IDNO, DRIVENO, CONTDATE, CONTAMT INTO v_idno, v_driveno, v_contdate, v_contamt
FROM OLD_DONATION2
WHERE IDNO = :new.idno;
IF :new.contamt < 50 THEN
RAISE_APPLICATION_ERROR (-20001, 'CONTRIBUTION TOO LOW FOR '
|| :new.idno || ' ' || :new.contamt);
END IF;
END;
/
SET SERVEROUTPUT OFF
another part
DECLARE
v_idno new_donation.idno%TYPE := '&in_idno';
v_driveno new_donation.driveno%TYPE := '&in_driveno';
v_contdate new_donation.contdate%TYPE := '&in_contdate';
v_contamt new_donation.contamt%TYPE := &in_contamt;
BEGIN
INSERT INTO new_donation
VALUES (v_idno, v_driveno, v_contdate, v_contamt);
END;
/
Im getting this error when I insert values at trigger2.
ERROR at line 1: ORA-04098: trigger 'XXXXXXXXX.NEW_DONATION' is
invalid and failed re-validation ORA-06512: at line 7
All im trying to do is insert some values input by the user to this new table, which is empty.
Also when donation amount is < 10 I want a error out.
Follow these steps.
Run this query select status from all_objects where object_name = 'TBL_USER_TRIGGER' and object_type = 'TRIGGER';
If the status is invalid run alter trigger trigger2 compile;
Then run show errors
It will throw the errors for you. You need to fix those.
Hope someone can help with this. I am new to triggers and I am trying to create a trigger that checks to see if the record being modified has a specific value.
example
I have a table called Filing that has a filing_id and a filing_status, I want to prevent someone from updating or deleting any records in that table has a filing_status="FILED".
so if i have the following
Filing_id Filing_status Val
--------- ------------- ---
0 Filed X
If someone tried to modify Val the trigger should stop it
I have created the following trigger:
CREATE or replace TRIGGER TRG_PREV_FILING
BEFORE DELETE or UPDATE
ON PF.FILING
FOR EACH ROW
declare
rowcnt number;
BEGIN
SELECT COUNT(filing_id) INTO rowcnt FROM PF.FILING
where status = 'FILED'
and filing_id = :new.filing_id;
if (rowcnt > 0)
then
raise_application_error (-20100, 'You can not delete Or Update initial record');
end if;
END;
The problem I am facing is I am getting:ORA-04091 which is "Table Filing is mutating, Trigger/function may not see it"
So basically I can't query on the same table that I am executing the trigger on? Is that the problem in my case and does anyone know a work around this?
I appreciate any help
You do not have to query the table trigger is firing on to be able to do that kind of check. You can get value of a column that is being modified using :old. Here is an example:
SQL> create table filing(
2 status varchar2(31),
3 val number
4 );
Table created
SQL> create or replace trigger TRG_FILING before delete or update on FILING
2 for each row
3 begin
4 if lower(:old.status) = 'filed'
5 then
6 raise_application_error(-20000, 'You cannot delete or modify this record');
7 end if;
8 end;
SQL> /
Trigger created
SQL> insert into FILING values('FILED', null);
1 row inserted
SQL> insert into filing values('OK', 1);
1 row inserted
SQL> commit;
Commit complete
SQL> select *
2 from filing;
STATUS VAL
------------------------------- ----------
FILED
OK 1
SQL> delete
2 from filing
3 where val is null;
ORA-20000: You cannot delete or modify this record
ORA-06512: at "HR.TRG_FILING", line 4
ORA-04088: error during execution of trigger 'HR.TRG_FILING'
The basic point is that you should design you database in a way that the trigger does its validation based on the updated/deleted row. If you have several rows with the same filing_id then you can overwork you database design. Maybe you really only check against the own table in which case you can use :old. But when you have several rows to check (which I assume because you make a count) then you have to use two tables. Here is a suggestion.
create table filing_status (filing_id number, status varchar2(10));
create table filing_content (filing_id number, content_id number, content varchar2(200));
CREATE or replace TRIGGER TRG_PREV_FILING
BEFORE DELETE or UPDATE
ON FILING_content
FOR EACH ROW
declare
rowcnt number;
BEGIN
SELECT COUNT(filing_id) INTO rowcnt FROM FILING_status
where status = 'FILED'
and filing_id = :new.filing_id;
if (rowcnt > 0)
then
raise_application_error (-20100, 'You can not delete Or update filed record');
end if;
END;
/
insert into filing_status values (1, 'FILING');
insert into filing_content values (1, 1, 'foo');
insert into filing_content values (1, 2, 'bar');
insert into filing_status values (1, 'FILED');
update filing_content set content = 'bahr' where filing_id = 1 and content_id = 2;
ERROR at line 1:
ORA-20100: You can not delete Or update filed record
ORA-06512: at "DEMO.TRG_PREV_FILING", line 9
ORA-04088: error during execution of trigger 'DEMO.TRG_PREV_FILING'