I am trying to write a trigger when I am receiving this error, it the first I encounter such error, and none of my searches lead to my case, kindly check
this is the trigger :
create or replace trigger TR_new_student
for insert on Students
compound trigger
IDs students.ID%type;
ID_user students.ID_u%type;
password users.password%type;
cand_m1 master.ID_master%type;
cand_m2 master.ID_master%type;
cand_m3 master.ID_master%type;
cand_password_m1 users.password%tyPe;
cand_password_m2 users.password%type;
cand_password_m3 users.password%type;
cand_password_m4 users.password%type;
username users.username%type;
type class_type is TABLE OF class%rowtype index by PLS_integer;
class_tab class_type;
part1 varchar2(12);
before each row
is
begin
:new.classes_accepte:='N';
:new.n_download:=0;
:new.n_msg_adm:=0;
:new.n_msg_res:=0;
:new.date_last_download:=sysdate;
:new.date_last_m_a:=sysdate;
:new.date_last_m_r:=sysdate;
:new.year_insc:=get_annee_scolaire;
:new.current_year:=get_annee_scolaire;
select ID_M1,ID_M2,ID_M3,password_m1,password_m2,password_m3,password_m4
into cand_m1,cand_m2,cand_m3,cand_password_m1,cand_password_m2,cand_password_m3,cand_password_m4
from candidate c
where c.ID_c=:new.ID_c and c.year=:new.year_insc;
password :=
CASE
when cand_M1=:new.current_master then cand_password_m1
when cand_M2=:new.current_master then cand_password_m2
when cand_M3=:new.current_master then cand_password_m3
else cand_password_m4
end;
if password != :new.first_pass then raise_application_error(-20003,'Le mot de passe n''est pas valide');
end if;
SELECT SUBSTR(:new.Fname, 1, 12) into part1 FROM DUAL;
username:=part1||TO_CHAR(:new.ID);
ID_user:=ID_u_seq.currval;
:new.ID_u:=ID_user;
ID_s:=ID_seq.nextval;
:new.ID:=ID_s;
insert
into users
values (ID_user,username,password,1);
insert
into inscriptions (year,date_insc,old_new,ID,master_ID,succeeded,password)
values (:new.year_insc,sysdate,'N',:new.ID,:new.current_master,:new.current_master,'P',password);
select *
BULK COLLECT INTO class_tab
from class d
where d.ID_master=:new.current_master and d.optionnel='N' and d.available='Y';
forall i in indices of class_tab
insert into Grades
values (class_tab(i).ID_class,:new.current_master,:new.ID,:new.year_insc,class_tab(i).graded,class_tab(i).credit,class_tab(i).semestre,class_tab(i).partial_flag,class_tab(i).final_1_flag,class_tab(i).project_flag,class_tab(i).final_2_flag,class_tab(i).partial_percent,class_tab(i).final_percent,class_tab(i).project_percent,'N','N','N','N','N',0,0,0,0,0,sysdate,:new.ID_u,'P',class_tab(i).description);
end before each row;
after statement
is
begin
execute immediate 'Create or replace view vue_stu'||IDs||'
as select province,circonscription,city,region,fam_status,cur_province,cur_cir,cur_region,cur_city,street,property,floor,phone,picture
from students
where ID_u='||ID_user||'';
execute immediate 'Grant update on vue_stu'||IDs||' to C##'||username||'';
end after statement;
end TR_new_student;
/
and this is the error I am receiving :
ORA-01775: looping chain of synonyms
can anyone tell me the possible source of this error or how I can fix it ?
thank you in advance :)
You should execute the following statement before compiling the trigger:
alter session set events '1775 trace name errorstack level 3;
Then compile the trigger. This will create a trace file. Provide a link to the created trace file.
Related
I am getting no data found error in pl/sql
Actually problem is in the trigger
I want to use NO_DATA_FOUND error in the elsif statement
alter table SUPPLIER add trusted_supplier VARCHAR(30)
and then update it with the data that I load in the table
UPDATE SUPPLIER SET trusted_supplier ='Exotic Liquids'
while using the select statement I have only one value in the trusted_supplier so if insert value in the product is not equal to that
trusted supplier value I am getting error as no data found
is there any way i use NO_DATA_FOUND error which is predefined in pl/sql in the else if statement
CREATE OR REPLACE TRIGGER CHECK_SUPPLIER
BEFORE INSERT ON PRODUCT
FOR EACH ROW
DECLARE
supplier_info varchar(400);
except1 exception;
begin
select SUPPLIER_NAME
INTO supplier_info
FROM PRODUCT P JOIN SUPPLIER S
ON S.COMPANY_NAME = P.SUPPLIER_NAME
where S.trusted_supplier = :NEW.SUPPLIER_NAME ; here
if(supplier_info = 'Exotic Liquids') then
dbms_output.put_line('trusted supplier')
elsif
---------here
END IF;
exception
when except1 then
dbms_output.put_line('its not the trusted supplier');
END;
/
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.
I am developing a trigger that keeps a table updated where the departments are recorded and the total sum of their salaries. Each time the EMP table is updated. The trigger updates this table. To avoid the error of mutant tables, I have implemented a "COMPOUND TRIGGER" type trigger.
Here I put your implementation:
CREATE OR REPLACE TRIGGER EJERCICIO_27
FOR INSERT OR UPDATE OR DELETE ON EMP
COMPOUND TRIGGER
TYPE t_DeptToUpdate IS TABLE OF NUMBER(2,0);
deptToUpdate t_DeptToUpdate;
-- AFTER EACH ROW Section:
AFTER EACH ROW IS
BEGIN
IF NOT :OLD.DEPTNO MEMBER OF deptToUpdate THEN
deptToUpdate.EXTEND;
deptToUpdate(deptToUpdate.LAST) := :OLD.DEPTNO;
END IF;
END AFTER EACH ROW;
-- AFTER STATEMENT Section:
AFTER STATEMENT IS
BEGIN
DBMS_OUTPUT.PUT_LINE('SE ACTUALIZAN UN TOTAL DE: ' || deptToUpdate.COUNT);
MERGE INTO SALARIO_DEPARTAMENTOS SD USING (
SELECT DEPTNO, NVL(SUM(SAL), 0) AS SUM_TOTAL FROM EMP
WHERE DEPTNO IN (SELECT * FROM TABLE(deptToUpdate))
GROUP BY DEPTNO
) D
ON (SD.DEPTNO = D.DEPTNO)
WHEN MATCHED THEN
UPDATE SET
SAL_TOT = D.SUM_TOTAL
WHERE SD.DEPTNO = D.DEPTNO
WHEN NOT MATCHED THEN
INSERT (DEPTNO, SAL_TOT)
VALUES(D.DEPTNO, D.SUM_TOTAL);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR : ' || SQLCODE || 'MENSAJE: ' || SQLERRM);
END AFTER STATEMENT;
END;
/
The problem I have when using the collection as a source for querying the MERGE statement. I understood that in Oracle 12c (I am using Oracle 12c R2 Enterprise) I could use the TABLE operator with locally defined types. As discussed in this post Using the TABLE Operator with Locally Defined Types in PL/SQL.
Exactly the errors that the compiler returns me are:
LINE/COL ERROR
-------- -----------------------------------------------------------------
20/5 PL/SQL: SQL Statement ignored
22/40 PL/SQL: ORA-22905: no se puede acceder a las filas de un elemento de tabla no anidada
22/46 PLS-00642: tipos de recopilaciĆ³n local no permitidos en sentencias SQL
Someone can tell me what approach to use without having to create any type in the schematic?. Thanks in advance.
EDIT
Translation of error messages:
ORA-22905: Rows of a non-nested table element can not be accessed
PLS-00642: Local collection types not allowed in SQL statements
Are you required to use a collection? If not, then it seems that a simple row-level trigger should work, something like this:
Create Or Replace Trigger emp_trig
After Insert Or Update Or Delete On emp
For Each Row
Begin
-- Subtract old salary from old department
Update department_salary ds
Set ds.tot_salary = ds.tot_salary - :old.salary
Where ds.dept = :old.dept;
If Inserting Or Updating Then
-- Add new salary to new department
Update department_salary ds
Set ds.tot_salary = ds.tot_salary + :new.salary
Where ds.dept = :new.dept;
If SQL%Rowcount = 0 Then
Insert Into department_salary (dept, tot_salary) Values(:new.dept, :new.salary);
End If;
End If;
End;
/
The separate update statements for old and new values handle cases when an updated row might contain a changed department number.
As LauDec commented, a very straightforward way to maintain a list of departments and total salary would be with a view such as this:
Create Or Replace View salario_departamentos_view As
Select deptno, Sum(sal) As sal_tot From emp
Group By deptno;
The beauty of this solution is that the data is always in perfect harmony with the EMP table, and it's very easy to understand, even for someone with minimal SQL skills.
I have got stuck in below and getting syntax error - Please help.
Basically I am using a collection to store few department ids and then would like to use these department ids as a filter condition while inserting data into emp table in FORALL statement.
Below is sample code:
while compiling this code i am getting error, my requirement is to use INSERT INTO table select * from table and cannot avoid it so please suggest.
create or replace Procedure abc(dblink VARCHAR2)
CURSOR dept_id is select dept_ids from dept;
TYPE nt_dept_detail IS TABLE OF VARCHAR2(25);
l_dept_array nt_dept_detail;
Begin
OPEN dept_id;
FETCH dept_id BULK COLLECT INTO l_dept_array;
IF l_dept_array.COUNT() > 0 THEN
FORALL i IN 1..l_dept_array.COUNT SAVE EXCEPTIONS
EXECUTE IMMEDIATE 'INSERT INTO stg_emp SELECT
Dept,''DEPT_10'' FROM dept_emp'||dblink||' WHERE
dept_id = '||l_dept_array(i)||'';
COMMIT;
END IF;
CLOSE dept_id;
end abc;
Why are you bothering to use cursors, arrays etc in the first place? Why can't you just do a simple insert as select?
Problems with your procedure as listed above:
You don't declare procedures like Procedure abc () - for a standalone procedure, you would do create or replace procedure abc as, or in a package: procedure abc is
You reference a variable called "dblink" that isn't declared anywhere.
You didn't put end abc; at the end of your procedure (I hope that was just a mis-c&p?)
You're effectively doing a simple insert as select, but you're way over-complicating it, plus you're making your code less performant.
You've not listed the column names that you're trying to insert into; if stg_emp has more than two columns or ends up having columns added, your code is going to fail.
Assuming your dblink name isn't known until runtime, then here's something that would do what you're after:
create Procedure abc (dblink in varchar2)
is
begin
execute immediate 'insert into stg_emp select dept, ''DEPT_10'' from dept_emp#'||dblink||
' where dept_id in (select dept_ids from dept)';
commit;
end abc;
/
If, however, you do know the dblink name, then you'd just get rid of the execute immediate and do:
create Procedure abc (dblink in varchar2)
is
begin
insert into stg_emp -- best to list the column names you're inserting into here
select dept, 'DEPT_10'
from dept_emp#dblink
where dept_id in (select dept_ids from dept);
commit;
end abc;
/
There appears te be a lot wrong with this code.
1) why the execute immediate? Is there any explicit requirement for that? No, than don't use it
2) where is the dblink variable declared?
3) as Boneist already stated, why not a simple subselect in the insert statement?
INSERT INTO stg_emp SELECT
Dept,'DEPT_10' FROM dept_emp#dblink WHERE
dept_id in (select dept_ids from dept );
For one, it would make the code actually readable ;)
So when I compile the following script it throws the mutating table error:
CREATE OR REPLACE TRIGGER SEND_MAIL
BEFORE DELETE
ON CATEGORIE
FOR EACH ROW
DECLARE
bodytext varchar2(100);
CURSOR getabonnee IS
SELECT CATEGORIEABONNEMENT.MAILABONNEEID, NAAM
FROM CATEGORIE
INNER JOIN CATEGORIEABONNEMENT ON CATEGORIEABONNEMENT.CATEGORIENAAM = CATEGORIE.NAAM
WHERE NAAM = :old.NAAM;
CURSOR getabonneeinfo(p_id CATEGORIEABONNEMENT.MAILABONNEEID%TYPE) IS
SELECT MAILABONNEE.VOORNAAM, MAILABONNEE.ACHTERNAAM, MAILABONNEE.EMAILADRES
FROM CATEGORIEABONNEMENT
INNER JOIN MAILABONNEE ON MAILABONNEE.ID = CATEGORIEABONNEMENT.MAILABONNEEID
WHERE MAILABONNEEID = p_id;
BEGIN
FOR cc IN getabonnee LOOP
FOR cc2 IN getabonneeinfo(cc.MAILABONNEEID) LOOP
bodytext := cc2.voornaam || cc2.achternaam || :old.NAAM;
EXECUTE IMMEDIATE bodytext;
SENDMAILABONNEE(bodytext, cc2.emailadres);
DELETE FROM CATEGORIEABONNEMENT
WHERE MAILABONNEEID = cc.MAILABONNEEID;
UPDATE NIEUWSBERICHT
SET CATEGORIENAAM = null
WHERE CATEGORIENAAM = :old.NAAM;
END LOOP;
END LOOP;
END;
/
savepoint deletecategory;
delete from Categorie where naam='Buitenland';
rollback to deletecategory;
I looked for solutions to this error and found that people suggest using a compound trigger. The problem is that for some reason, my database software (Oracle SQL Developer) doesn't recognize the compound trigger as a valid unit. I checked my oracle version and I have 11g so I don't quite understand why it won't work. Anyone has a solution for this problem?
In your first cursor, do not include the CATEGORIE table in the query. The trigger already has the values for all of the columns in the rows you are deleting, so change it to:
SELECT MAILABONNEEID
FROM CATEGORIEABONNEMENT
WHERE CATEGORIEABONNEMENT.CATEGORIENAAM = :old.NAAM;