Help with mutating trigger
Hi all,
I have a trigger shown below is gives me the mutating error.
The error is to do with the select from the payments table (which it does not allow cos of the mutating effect).
Can anyone give me some ideas on how I can resolve this?
Thank you very much
create or replace
TRIGGER CHECK_INVOICE_PAID_IN_FULL AFTER
UPDATE OF "PAYMENT_STATUS" ON "PAYMENTS" FOR EACH ROW
DECLARE
-- check if all the payments for that invoice have been received (completed)
-- if they are complete then sum all the payments for that invoice and compare to gross value on invoices
v_invoice_amount NUMBER;
v_gross_amount NUMBER;
thisproc CONSTANT VARCHAR2(80) := 'trap_errmesg for mco_transfer_status';
v_value VARCHAR2(150);
BEGIN
-- Changed as requested by nicola on 12/09/05 IF :NEW.PAYMENT_COMPLETE is not null
IF :NEW.PAYMENT_STATUS='C' AND :NEW.PAYMENT_STATUS <> :OLD.PAYMENT_STATUS
THEN
-- We will sum all the payments for that invoice that have a value in payment_complete
SELECT NVL(SUM(amount),0) INTO v_invoice_amount FROM payments WHERE payment_complete IS NOT NULL AND invoice_id=:NEW.INVOICE_ID;
-- We will also get the gross amount for the invoice to compare to the sum of the payments
SELECT NVL(gross,0) INTO v_gross_amount FROM invoices WHERE
invoice_id = :NEW.INVOICE_ID ;
END IF;
IF v_invoice_amount = v_gross_amount
THEN
UPDATE invoices SET paid_date=SYSDATE;
END IF;
END CHECK_INVOICE_PAID_IN_FULL;
/
Hi Anwar,
Thanks for this.
Can you please help me out here
I did the bit with creating a temporay table to hold the invoice_ids and then created a procedure that will be fired by the trigger, however because of the clause
IF :NEW.PAYMENT_STATUS='C' AND :NEW.PAYMENT_STATUS <> :OLD.PAYMENT_STATUS
I cant use a statement level trigger for this procedure and when i use the row level trigger i get the mutating error.
here is what i have got so far.
A temporary table which holds the invoice_ids (this is called payments_temp)
I then created a trigger as below which populates this temp table
TRIGGER insert_payments_temp BEFORE
UPDATE OF "PAYMENT_STATUS" ON "PAYMENTS" FOR EACH ROW
DECLARE
-- check if all the payments for that invoice have been received (completed)
-- if they are complete then sum all the payments for that invoice and compare to gross value on invoices
v_invoice_amount NUMBER;
v_gross_amount NUMBER;
thisproc CONSTANT VARCHAR2(80) := 'trap_errmesg for mco_transfer_status';
v_value VARCHAR2(150);
BEGIN
INSERT INTO payments_temp
VALUES (:NEW.invoice_id);
END insert_payments_temp;
Then I created a procedure below which will do the update
PROCEDURE "CHECK_INVOICE_PAID_IN_FULL_PRO" IS
-- check if all the payments for that invoice have been received (completed)
-- if they are complete then sum all the payments for that invoice and compare to gross value on invoices
v_invoice_amount NUMBER(20);
v_gross_amount NUMBER;
thisproc CONSTANT VARCHAR2(80) := 'trap_errmesg for mco_transfer_status';
v_value VARCHAR2(150);
BEGIN
-- We will sum all the payments for that invoice that have a value in payment_complete
--select sum(amount) into v_invoice_amount from payments where payment_complete is not null and invoice_id=:NEW.INVOICE_ID;
FOR x IN (SELECT invoice_id FROM payments_temp) LOOP
SELECT NVL(SUM(amount),0) INTO v_invoice_amount FROM payments
WHERE payment_complete IS NOT NULL AND invoice_id=X.INVOICE_ID;
SELECT NVL(gross,0) INTO v_gross_amount FROM INVOICES WHERE
invoice_id = X.INVOICE_ID ;
END LOOP;
-- We will also get the gross amount for the invoice to compare to the sum of the payments
IF v_invoice_amount = v_gross_amount
THEN
UPDATE INVOICES SET paid_date=SYSDATE;
COMMIT;
END IF;
END CHECK_INVOICE_PAID_IN_FULL_PRO;
Then finally the update trigger itself below
TRIGGER "NICKC"."CHECK_INVOICE_PAID_IN_FULL" AFTER
UPDATE OF "PAYMENT_STATUS" ON "NICKC"."PAYMENTS" FOR EACH ROW
DECLARE
-- check if all the payments for that invoice have been received (completed)
-- if they are complete then sum all the payments for that invoice and compare to gross value on invoices
v_invoice_amount NUMBER;
v_gross_amount NUMBER;
thisproc CONSTANT VARCHAR2(80) := 'trap_errmesg for mco_transfer_status';
v_value VARCHAR2(150);
BEGIN
-- Changed as requested by nicola on 12/09/05 IF :NEW.PAYMENT_COMPLETE is not null
IF :NEW.PAYMENT_STATUS='C' AND :NEW.PAYMENT_STATUS <> :OLD.PAYMENT_STATUS
THEN
CHECK_INVOICE_PAID_IN_FULL_PRO;
END IF;
END CHECK_INVOICE_PAID_IN_FULL;
However i still get the mutating error message and when i try to create a statemtnet level trigger i get the message say cannot use :NEW and :OLD values here.
Please help
Similar Messages
-
Need help with create trigger based on more then 1 table and join.
Hello,
Here i have 3 tables
1. Employee
PERSON_ID
1
1
N
NUMBER
None
ORG_ID
2
N
NUMBER
Frequency
LOC_ID
3
N
NUMBER
Frequency
JOB_ID
4
Y
NUMBER
Height Balanced
FLSA_STATUS_ID
5
Y
NUMBER
Frequency
FULL_NAME
6
N
VARCHAR2 (250 Byte)
Height Balanced
FIRST_NAME
7
N
VARCHAR2 (20 Byte)
Height Balanced
MIDDLE_NAME
8
Y
VARCHAR2 (60 Byte)
Height Balanced
LAST_NAME
9
N
VARCHAR2 (40 Byte)
Height Balanced
PREFERRED_NAME
10
Y
VARCHAR2 (80 Byte)
None
EMAIL
11
Y
VARCHAR2 (250 Byte)
None
MAILSTOP
12
Y
VARCHAR2 (100 Byte)
None
HIRE_DATE
13
N
DATE
None
2. ems_candidate
EMS_CANDIDATE_ID
1
1
N
NUMBER
None
EMS_JOB_ID
2
Y
NUMBER
Frequency
NAME
3
N
VARCHAR2 (255 Byte)
Frequency
EMAIL
4
Y
VARCHAR2 (255 Byte)
None
TELEPHONE
5
Y
VARCHAR2 (25 Byte)
None
EMS_SOURCE_ID
6
Y
NUMBER
Frequency
RECEIVED_DATE
7
Y
DATE
Frequency
COMMENTS
8
Y
VARCHAR2 (4000 Byte)
None
3. employee_resources
EMP_RES_ID
1
1
N
NUMBER
None
PERSON_ID
2
Y
NUMBER
Height Balanced
CANDIDATE_ID
3
Y
NUMBER
Frequency
EMP_START_DATE
4
Y
DATE
None
CUSTOM_RESOURCE_FLAG
5
Y
NUMBER (1)
None
RESOURCE_GROUP_ID
6
N
NUMBER
Frequency
RESOURCE_STATUS_ID
7
N
NUMBER
Frequency
GROUP_LOC_ID
8
N
NUMBER
Height Balanced
ASSIGNED_JIRA
9
Y
VARCHAR2 (250 Byte)
None
REVOKED_JIRA
10
Y
VARCHAR2 (250 Byte)
None
CREATED_DATE
11
Y
DATE
SYSDATE
None
UPDATED_DATE
12
Y
DATE
None
Now i want to create trigger when new record get inserted in employee table wanted to update person_id in employee_resources table.
So i want to match ems_candidate.name with employee.full_name , ems_candidate.ems_job_id with employee.ems_job_id. And if it matched then update person_id in employee_resources table else through an exception and insert record in temp table.
If anybody has an idea can u please help me.
Thanks,
Gayatri.I created below trigger
CREATE TRIGGER emp_resources_upd_person_id
AFTER INSERT ON ems.employee
FOR EACH ROW
BEGIN
UPDATE ems.employee_resources
SET person_id = :new.person_id
WHERE candidate_id = (SELECT ems_candidate_id
FROM ems.ems_candidate cand, ems.employee emp
WHERE TRIM(UPPER(emp.first_name)) = TRIM(UPPER(SUBSTR (cand.name, 1, INSTR (cand.name, ' ') - 1)))
AND TRIM(UPPER(emp.last_name)) = TRIM(UPPER(SUBSTR (cand.name,INSTR (cand.name, ' ') + 1,DECODE (INSTR (SUBSTR (cand.name, INSTR (cand.name, ' ') + 1), ' '),0,LENGTH (cand.name),(INSTR (SUBSTR (cand.name, INSTR (cand.name, ' ') + 1), ' ') - 1)))))
AND emp.person_id = :new.person_id);
EXCEPTION
WHEN OTHERS THEN
INSERT INTO ems.update_person_id_exception(person_id,first_name,last_name,full_name) VALUES(:new.person_id,:new.first_name,:new.last_name,:new.full_name);
END;
Now when i am trying to insert row in ems.employee table it gives me an error
ORA-04091
table string.string 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.
Can anybody please help me to come out from these error.
Thanks,
Gayatri. -
I need help with a trigger mutating a table
I'll add the trigger I have written now at the bottom. Here is the problem that I have. We have employees and their families in an individual table.
A family is indicated by matching client, branch, and emp_id. An employee is indicated by individual_num = 1. All other numbers indicate family members. A person is determined to be terminated by having a date other than '2299/12/31' (It's a varchar(10) and very very wrong. Don't ask...) in the termination date column.
A family member can be terminated in the system independent of the rest of there family. However, if an employee is terminated then all active family members need the termination date set to the same date as the employee. If that termination date is then changed for the employee all family members with the same date need to have their dates updated.
I understand why this causes table mutation but I need a work around. Any ideas? Please...
CREATE OR REPLACE TRIGGER INDIV_EMP_TERM
after update on INDIVIDUAL
for each row
begin
if ( :new.INDIVIDUAL_NUM = 1 and :old.TERMINATION_DATE <> :new.TERMINATION_DATE ) then
if ( :old.TERMINATION_DATE = '2299/12/31' ) then
update INDIVIDUAL
set TERMINATION_DATE = :new.TERMINATION_DATE
where CLIENT = :new.CLIENT
and BRANCH = :new.BRANCH
and EMP_ID = :new.EMP_ID
and INDIVIDUAL_NUM <> 1
and TERMINATION_DATE = '2299/12/31';
else
update INDIVIDUAL
set TERMINATION_DATE = :new.TERMINATION_DATE
where CLIENT = :new.CLIENT
and BRANCH = :new.BRANCH
and EMP_ID = :new.EMP_ID
and INDIVIDUAL_NUM <> 1
and TERMINATION_DATE = :old.TERMINATION_DATE;
end if;
end if;
end;Try your code like this below .It will help you to eliminate the mutating error
create or replace PACKAGE test_update IS
type row_type is table of rowid index by binary_integer;
v_row row_type ;
v_index binary_integer ;
v_num integer := 0 ;
flag integer := 1 ;
END;
create or replace trigger test_up
before update
on test123
begin
select USR_ID
into test_update.v_num
from test123 ;
dbms_output.put_line ( 'before update '||test_update.v_num );
test_update.v_index := 0;
end ;
create or replace trigger test_up_after
after update
on test123
begin
dbms_output.put_line ( test_update.v_index );
test_update.flag := 0 ;
for i in 1 .. test_update.v_index loop
update test123
set UPD_BY = nvl(test_update.v_num ,0),
UPD_DATE = sysdate
where rowid = test_update.v_row(i) ;
end loop ;
test_update.flag := 1 ;
test_update.v_index := 0;
end ;
create or replace trigger test_1
after update on test123
for each row
begin
-- dbms_output.put_line ( 'after update test flag '||test_update.flag );
if test_update.flag = 1 then
test_update.v_index := test_update.v_index + 1 ;
test_update.v_row(test_update.v_index) := :old.rowid ;
end if ;
end ; -
Help with Mutating Error / Trigger / APEX_MAIL.SEND
Hello:
I am trying to get the apex_mail.send function working when a user submits a new record. I've taken the code from the canned Issue Tracker application code and tried to retrofit it to my tables. However I keep receiving a mutating error on line 11 (line 11 bolded below):
-- Create a trigger that will send notification after
-- the submission of each new idea on the IDEAS table.
create or replace trigger IDEAS_EMAIL
AFTER
insert or update on IDEAS
for each row
begin
IF (INSERTING AND :new.manager_name IS NOT NULL)
OR
(UPDATING AND (:old.manager_name IS NULL OR :new.manager_name != :old.manager_name) AND :new.manager_name IS NOT NULL) THEN
FOR c1 IN
(SELECT fname, lname, email
FROM ideas_users
WHERE fname||' '||lname = :new.manager_name)
LOOP
IF c1.email IS NOT NULL THEN
FOR c2 IN
(SELECT MANAGER_NAME, FULL_TEXT, SUMMARY, ID, SUBMITTED_BY, SUB_DATE
FROM IDEAS
WHERE ID = :new.ID)
LOOP
APEX_MAIL.SEND(
p_to => c1.email,
p_from => c1.email,
p_body =>
'This is a test message. ' ||chr(10)||
p_subj => 'test email subject');
END LOOP;
END IF;
END LOOP;
END IF;
end;
I've also tried setting the following line to the primary key field in the user table and inserting that value into the main table but I still get the same error message.
WHERE fname||' '||lname = :new.manager_name)
Thanks in advance for any help.Let me get this straight..
You are inserting a row into a table, and when you insert the row, you are going to send an e-mail t o the manager who owns this row. In the data you are inserting, is the e-mail address of the manager, right?
David has provided the answer for you.. Use the :NEW.Email value instead of creating a cursor and all that.. Unless you have a need to e-mail multiple people of the record insert..
Thank you,
Tony Miller
Webster, TX
On the road of life...There are 'windshields', and there are 'bugs'
(splat!)
"Squeegees Wanted" -
Please help! I'm an experienced LabVIEW user, and this one is really
puzzling me. I have a (2) PXI-6070E MIO boards in a PXI chassis. What
I would like to do is have a data acquisition with an analog trigger.
When the analog trigger occurs, I would like to start a counter output.
Therefore, I'd really like to route the "AI start trigger" to the
counter gate. Here's what has happened:
1) I can set up the analog trigger, and the data acquires when the
signal goes above the trigger level, just like it's supposed to.
2) I can manually start the counter by toggling the gate by hand and
watching the output toggle.
3) I can't get the AI start trigger to be an output to anything--a PFI,
an RTSI line, nothing. How do I route this
signal to gate of the
counter? If I can get that, I know it will work.
Mark
Sent via Deja.com http://www.deja.com/
Before you buy.I got it. Although I am not able to specifically route the AI trigger
to a PFI, I can route it to a RTSI line. (If I'm triggering a counter
from the same MIO board, I don't even need to do that--see below).
What I found is that buried deep in the Generate Delayed Pulse VI, there
is a VI called CTR Mode Config. There is a cluster input to this VI
which contains the parameter for the gate line. If you check the help
on this VI, it shows that you can select AI trigger or RTSI line as the
counter gate, but the cluster control they've put on the front panel
doesn't allow you to select this as an option.
Redoing the control (basically deleting it, putting the wiring tool on
the input, and then selecting create control) allowed me to select the
appropr
iate line, and thus my triggering worked correctly. I don't know
why they don't have all the selections available in the control they
made for this VI. *shrug*
Mark
In article <8ra9nl$nh0$[email protected]>,
[email protected] wrote:
> Please help! I'm an experienced LabVIEW user, and this one is really
> puzzling me. I have a (2) PXI-6070E MIO boards in a PXI chassis.
What
> I would like to do is have a data acquisition with an analog trigger.
> When the analog trigger occurs, I would like to start a counter
output.
> Therefore, I'd really like to route the "AI start trigger" to the
> counter gate. Here's what has happened:
>
> 1) I can set up the analog trigger, and the data acquires when the
> signal goes above the trigger level, just like it's supposed to.
>
> 2) I can manually start the counter by toggling the gate by hand and
> watching the output toggle.
>
> 3) I can't get the AI start trigger to be an output to anything--a
PFI,
> an RTSI line, nothing. How do I ro
ute this signal to gate of the
> counter? If I can get that, I know it will work.
>
> Mark
>
> Sent via Deja.com http://www.deja.com/
> Before you buy.
>
Sent via Deja.com http://www.deja.com/
Before you buy. -
CREATE or REPLACE TRIGGER tr_Leave_audit_insert
AFTER INSERT
ON att_hr_dev.leaves FOR EACH ROW
DECLARE
v_username varchar2(20);
d_date date;
BEGIN
SELECT user INTO v_username FROM dual;
select sysdate into d_date from dual;
insert into att_he.leaves_audit (leave_id, update_user, leave_type, leave_start_date, leave_end_date, half_day_flag, total_days, note_to_manager, change_date )
values (:new.leave_id, v_username, :new.leave_type, :new.leave_start_date, :new.leave_end_date, :new.half_day_flag, :new.total_days, :new.note_to_manager, d_date);
END;Hi
My trigger isnt working, i am getting the error
Missing IN OUT parameter at Index:: 1
could anyone help please
thanksHi,
the code works for me. The error message you posted here doesn't sound like an Oracle error -- what is the Oracle errorcode (ORA-xxxxx) that you are getting?
Best regards,
Nikolay -
Need help with creating trigger using instead of insert.
Hi all,
I am trying to create a trigger that can read the inserted Mail data from table1 and check if the Mail data matches the Mail data from table2. If the Mail data matches the Mail data from table2 it will get the EmpID from table2 and insert it into table1
column EmpID.
Here are table2 columns:
EmpID (int) Mail(varchar) Mail2(varchar)
101 [email protected] [email protected]
102 [email protected] [email protected]
table1 columns
EmpID (int)(primary key) Mail(varchar) Mail2(varchar)
If I insert [email protected] into table1 column Mail, I would like it to get the value for the EmpID from table2 before actually inserting the record into table1, by matching the Mail from table1 = Mail from table2.
I am using ASP.Net to insert the records into Mail and Mail2.
How can I achieve that?
I appreciate any help.There should be two SQL statements in the stored procedure in order to accomplish the task?
Ideally you need to include logic as a part of your insert procedure itself. You should have a standard insert stored procedure which should include this logic and should be used for all inserts.
Also if EmpID field has to have a non NULL value always you may better off creating a foreign key constraint from Table1 to Table2 on EmpID column to enforce the Referential Integrity.
Please Mark This As Answer if it helps to solve the issue Visakh ---------------------------- http://visakhm.blogspot.com/ https://www.facebook.com/VmBlogs -
I added the trigger logic to the before insert trigger created by designer and it works a treat.
Thankyou so much, I've learned bundles, hopefully I'll be abe to impart my knowledge to others in the future!Like to know the final trigger.
Can you please post it.
~Chandru -
Trigger in mutation - Update another rows in the same table with a trigger
Hi ,
I try to do a before update trigger on a table , but the trigger is in mutation. I understand why it do that but my question is :
How can I update other rows in the same table when a UPDATE is made on my table??????
Here is my trigger :
CREATE OR REPLACE TRIGGER GDE_COMPS_BRU_5 BEFORE
UPDATE OF DEPARTEMENT--, DISCIPLINE, DEG_DEMANDE, CE_ETAB
ON GDEM.COMPOSITION_SUBV
FOR EACH ROW
Organisme : FQRNT-FQRSC
Date de création : 14-07-2011
Date de modification :
Modifié par :
Auteur : Johanne Plamondon
Description : Ce déclencheur s'executera lors de la modification
du responsable dans la table COMPOSITION_SUBV
DECLARE
V_OSUSER V$SESSION.OSUSER%TYPE;
V_PROGRAM V$SESSION.PROGRAM%TYPE;
V_TERMINAL V$SESSION.TERMINAL%TYPE;
V_MACHINE V$SESSION.MACHINE%TYPE;
V_MODULE V$SESSION.MODULE%TYPE;
V_LOGON_TIME V$SESSION.LOGON_TIME%TYPE;
V_AUDIT_ID NUMBER;
vSEQ NUMBER;
i NUMBER;
vID DEMANDE.ID%TYPE;
BEGIN
begin
SELECT OSUSER, PROGRAM, TERMINAL,MACHINE,MODULE, LOGON_TIME
INTO V_OSUSER,V_PROGRAM,V_TERMINAL,V_MACHINE,
V_MODULE,V_LOGON_TIME
FROM V$SESSION
WHERE TYPE = 'USER'
AND USERNAME = USER
AND LAST_CALL_ET IN (0,1)
AND ROWNUM < 2;
exception when others then null; end;
IF NVL(:NEW.SC_PART,' ') = 'CHC' THEN
SELECT COUNT(*)
INTO i
FROM DEMANDE
WHERE DEM_REF = :NEW.DEM_ID
AND PER_NIP = :NEW.PER_NIP;
IF i = 1 THEN
SELECT ID
INTO vID
FROM DEMANDE
WHERE DEM_REF = :NEW.DEM_ID
AND PER_NIP = :NEW.PER_NIP;
UPDATE COMPOSITION_SUBV
SET --CE_ETAB = :NEW.CE_ETAB,
--DISCIPLINE = :NEW.DISCIPLINE,
DEPARTEMENT = :NEW.DEPARTEMENT,
--DEG_DEMANDE = :NEW.DEG_DEMANDE,
DATE_MODIF = SYSDATE,
USER_MODIF = V_OSUSER
WHERE DEM_ID = vID
AND PER_NIP = :NEW.PER_NIP
AND ANNEE = :NEW.ANNEE;
END IF;
END IF;
/*EXCEPTION
WHEN OTHERS THEN
NULL;*/
END;A standard disclaimer, the mutating trigger error is telling you that you really, really, really don't want to be doing this. It generally indicates a major data model problem when you find yourself in a situation where the data in one row of a table depends on the data in another row of that same table. In the vast majority of cases, you're far better off fixing the data model than in working around the problem.
If you are absolutely sure that you cannot fix the data model and must work around the problem, you'll need
- A package with a collection (or global temporary table) to store the keys that are modified
- A before statement trigger that initializes the collection
- A row-level trigger that adds the keys that were updated to the collection
- An after statement trigger that iterates over the data in the collection and updates whatever rows need to be updated.
If you're on 11g, this can be simplified somewhat by using a compound trigger with separate before statement, row-level, and after statement sections.
Obviously, though, this is a substantial increase in complexity over the single trigger you have here. That's one of the reasons that it's generally a bad idea to work around mutating table exceptions.
Justin -
Help with Oracle Table Audit Trigger
Hi Guys,
Need some help with the design of a trigger please. I have created one standard audit table where all sensitive data will be audited/inserted. The idea is to insert the column name and the old and new values here, dont want to maintain an audit table for each and every table there is, reporting would be a nightmare.
Trying to fetch all the column names from sys objects then looping through each and inserting the new and old values for them into the audit table. Everything else is fine apart from the actual :old and :new value inserts. The column name is coming from a variable in a cursor and this is where I seem to be failing.
Can anyone help please? What is the correct syntax to use?
CREATE OR REPLACE TRIGGER commission_update
AFTER UPDATE
ON commission
FOR EACH ROW
DECLARE
v_username varchar2(10);
v_column varchar2(20);
-- Get Table Columns
cursor table_column is
select c.name
from sys.col$ c, sys.obj$ t
where t.obj# = c.obj#
and t.type# in (2, 3, 4)
and bitand(c.property, 32) = 0 /* not hidden column */
and t.name = 'COMMISSION';
BEGIN
-- Find username of person performing UPDATE into table
SELECT user
INTO v_username
FROM dual;
open table_column;
loop
fetch table_column
into v_column;
EXIT WHEN table_column%NOTFOUND;
-- Insert record into audit_record
INSERT INTO audit_record
( aud_code,
aud_ban_code,
aud_user,
aud_table,
aud_column,
aud_old_val,
aud_new_val,
aud_date )
VALUES
( xaudit_record.nextval,
:old.com_ban_code,
v_username,
'COMMISSION',
v_column,
:old.v_column, /* problem here!!!!!!! */
:new.v_column, /* problem here!!!!!!! */
sysdate );
end loop;
close table_column;
END;
/What does auditing mean in the financial environment? "An audit is a professional, independent examination of a company's financial statements and accounting documents according to generally accepted accounting principles."
What does it mean in database terms? Surely, the basic definition would be the same, ito of a proper independent examination of changes in the database according to accepted principles?
And just how does a trigger live up to that? When it is fully dependent on being enabled for that transaction in order to examine it? It is trivial to disable a trigger, make changes, and re-enable it.
So what happens to your "auditing" then?
Do you really think that a trigger suffices as a means to audit changes in a table? And if so, what logic and reasoning do you use to discard Oracle's auditing features that are built into the core of the database? -
Who can help me with a trigger?
Hi, I've got a little problem with my database that can be solved, or so I think, with a trigger. First of all, here it is.
create table utente
(id_utente number(5) Primary Key,
nome varchar2(40),
sexo varchar2(1) check (sexo='M' or sexo='F'),
morada varchar2(60),
data_nascimento date,
contacto number(10),
numero_BI number(9)
create table servico
(id_servico number(5) Primary Key,
descricao varchar2(40),
valor number(5),
data_inicio date,
data_fim date,
check (data_fim>=data_inicio)
create table modalidade
(id_modalidade number(5) Primary Key,
descricao varchar2(40),
valor number(5)
create table quarto
(id_quarto number(5) Primary Key,
descricao varchar2(40)
create table inscricao_servico
(id_inscricao_servico number(5) Primary Key,
id_servico number(5) references servico(id_servico),
id_utente number(5) references utente(id_utente),
data_pagamento date
create table inscricao_mod
(id_inscricao_mod number(5) Primary Key,
id_modalidade number(5) references modalidade(id_modalidade),
id_utente number(5) references utente(id_utente),
data_inscricao date,
data_cessacao date,
check (data_cessacao>data_inscricao)
create table estadia
(id_estadia number(5) Primary Key,
id_utente number(5) references utente(id_utente),
id_quarto number(5) references quarto(id_quarto),
data_entrada date,
data_saida date,
check (data_saida>data_entrada)
create table pag_mensalidade
(id_pag_mensalidade number(5) Primary Key,
id_inscricao_mod number(5) references inscricao_mod(id_inscricao_mod),
mes_mensalidade number(6),
data_pagamento date,
unique (id_inscricao_mod,mes_mensalidade)
insert into utente values (1, 'Manel', 'M', 'Rua grande', to_date('80.02.02','yy.mm.dd'), 123456789, 687654321);
insert into utente values (2, 'Artur', 'M', 'Rua pequena', to_date('81.03.04', 'yy.mm.dd'), 223456789, 587654321);
insert into utente values (3, 'Cardoso', 'M', 'Rua estreita', to_date('82.04.05', 'yy.mm.dd'), 323456789, 487654321);
insert into utente values (4, 'Abílio', 'M', 'Rua larga', to_date('79.07.06', 'yy.mm.dd'), 423456789, 787654321);
insert into utente values (5, 'Antunes', 'M', 'Rua nova', to_date('79.06.01', 'yy.mm.dd'), 423557982, 387654822);
insert into modalidade values (1, 'Total', 800);
insert into modalidade values (2, 'Diurno ate 18', 600);
insert into modalidade values (3, 'Diurno ate 22', 700);
insert into quarto values (1, '412');
insert into quarto values (2, '413');
insert into quarto values (3, '414');
insert into quarto values (4, '415');
insert into quarto values (5, '416');
insert into servico values (1, 'Excursao a Fatima', 150, to_date('11.05.13', 'yy.mm.dd'), to_date('11.05.13', 'yy.mm.dd'));
insert into servico values (2, 'Ida ao cinema', 10, to_date('11.08.22', 'yy.mm.dd'), to_date('11.08.22', 'yy.mm.dd'));
insert into servico values (3, 'Apoio domiciliario', 100, to_date('11.07.01', 'yy.mm.dd'), to_date('11.07.31', 'yy.mm.dd'));
insert into estadia values (1, 1, 1, to_date('11.05.05', 'yy.mm.dd'), null);
insert into estadia values (2, 2, 2, to_date('10.01.02', 'yy.mm.dd'), null);
insert into estadia values (3, 3, 3, to_date('09.12.15', 'yy.mm.dd'), to_date('10.02.25', 'yy.mm.dd'));
insert into inscricao_mod values (1, 1, 1, to_date('09.12.15', 'yy.mm.dd'), null);
insert into inscricao_mod values (2, 2, 2, to_date('11.11.11', 'yy.mm.dd'), null);
insert into inscricao_mod values (3, 1, 3, to_date('10.04.10', 'yy.mm.dd'), to_date('11.09.21', 'yy.mm.dd'));
insert into inscricao_servico values (1, 1, 1, null);
insert into inscricao_servico values (2, 1, 2, to_date('11.10.01', 'yy.mm.dd'));
insert into inscricao_servico values (3, 2, 4, null);
insert into inscricao_servico values (4, 3, 5, to_date('10.12.12', 'yy.mm.dd'));
insert into pag_mensalidade values (1, 1, 201110, to_date('11.10.23', 'yy.mm.dd'));
insert into pag_mensalidade values (2, 2, 201111, to_date('11.11.27', 'yy.mm.dd'));
insert into pag_mensalidade values (3, 3, 201112, NULL);
insert into pag_mensalidade values (4, 1, 201111, NULL);
drop table estadia;
drop table inscricao_servico;
drop table quarto;
drop table pag_mensalidade;
drop table inscricao_mod;
drop table modalidade;
drop table servico;
drop table utente;
Ok, the problem is: the table 'estadia' means 'stay' and 'quarto' means 'room'. I need to, before inserting on 'estadia', to check if that room is available or not.
I may do this:
insert into estadia values (4, 3, *3*, to_date('09.12.15', 'yy.mm.dd'), to_date('10.02.25', 'yy.mm.dd'));
but if, after this command, i do this:
insert into estadia values (5, 4, *3*, to_date('09.12.15', 'yy.mm.dd'), to_date('10.02.25', 'yy.mm.dd'));
it accepts and it shouldn't, because I'm putting users 3 and 4 in the same room at the same time.
the 3rd field is the room id and the next two fields are the entry date (data_entrada) and the exit date (data_saida). If a room is related to a specific stay in which the exit date hasn't gone by, that room shouldn't be available. Also, the exit date may be null, meaning that the exit date is unknown.
I hope to have been clear about my question.
Thanks, ChiapaThis is a fairly easy business rule which can be implemented using triggers. Here goes.
Tables (my version):
drop table stay;
drop table room;
create table room
(room_id number not null primary key
,other_col varchar2(10) not null)
create table stay
(stay_id number not null primary key
,room_id number not null references room(room_id)
,arrive date not null check(trunc(arrive)=arrive)
,depart date check(trunc(depart)=depart)
,unique (room_id,arrive)
,check(arrive <= depart))
/The rule you are trying to implement is this one:
create or replace assertion no_overlap as
check(not exists
(select 'two overlapping stays for a room'
from stay s1
,stay s2
where s1.room_id = s2.room_id
and s1.stay_id != s2.stay_id
and (s1.arrive between s2.arrive and nvl(s2.depart,s1.arrive) or
s1.depart between s2.arrive and nvl(s2.depart,s1.depart))
/Sometime in a far far away future, we may be done now. That future has still to arrive though.
So here's the trigger-code and supporting objects:
create or replace procedure p_get_Lock(p_lockname in varchar2) as
pl_id number(38);
pl_return number;
pl_timeout number := 0;
begin
-- Go get a unique lockhandle for this lockname.
pl_id := dbms_utility.get_hash_value(name => p_lockname
,base => 1
,hash_size => power(2,30));
-- Request the application lock in X-mode.
pl_return :=
dbms_lock.request(id => pl_id
,lockmode => dbms_lock.ssx_mode
,timeout => pl_timeout
,release_on_commit => true);
if pl_return not in (0,4)
then
raise_application_error(-20000,'Could not serialize for business rule.');
end if;
end;
create global temporary table stay_te
(room_id number not null)
create or replace trigger stay_aiur
after insert or update on stay
for each row
begin
if inserting or
(updating and (:new.room_id != :old.room_id or
:new.arrive < :old.arrive or
:new.depart > :old.depart))
then
insert into stay_te(room_id) values(:new.room_id);
end if;
end;
create or replace trigger stay_aius
after insert or update on stay
begin
for r in (select distinct room_id
from stay_te)
loop
p_get_lock(to_char(r.room_id));
declare
p_error varchar2(80);
begin
select 'Overlapping stays for room '||to_char(r.room_id)||'.'
into p_error
from dual
where exists
(select 'two overlapping stays for a room'
from stay s1
,stay s2
where s1.room_id = s2.room_id
and s1.room_id = r.room_id
and s1.stay_id != s2.stay_id
and (s1.arrive between s2.arrive and nvl(s2.depart,s1.arrive) or
s1.depart between s2.arrive and nvl(s2.depart,s1.depart))
raise_application_error(-20000,p_error);
exception when no_data_found then
null;
end;
end loop;
delete from stay_te;
end;
/Haven't tested it, but I think it's all okay. -
Hi All,
i have a few triggers that I need your help with.
The first trigger (below) is fired when an update is made to a column in an invoices table.
The question I have is to do with the exception section.
I want to change the status of the invoice if it fails to F.
This trigger has already complied fine but I have no test data yet to test against.
I was unsure of the mutating effect and was wondering if the exception section will cause it to mutate since it is trying to update the status of the current invoice being processed. Pls see below
create or replace
TRIGGER POP_STAGE AFTER
UPDATE OF "EXCHANGED_DATE" ON "INVOICES" FOR EACH ROW
declare
-- check if the invoice.mco_transfer_status has been updated to R
-- if it has then insert records into the relevant tables
-- Start the Insert based on the update of invoices.mco_transfer_status
ecode varchar2(100);
thisproc CONSTANT VARCHAR2(80) := 'trap_errmesg for transfer_status';
v_value varchar2(150);
BEGIN
-- do updates based on update of invoices.transfer_status column
IF :NEW.TRANSFER_STATUS='R'
THEN
-- Insert into E_TICKET_STAGE
INSERT INTO E_TICKET_STAGE (emco_document_number,exchanged_date,pax_seq,pax_forename,pax_surname,pax_type)
select :NEW.EMCO_DOCUMENT_NUMBER, :NEW.EXCHANGED_DATE,PX.PAX_SEQ,PX.FORENAME,PX.SURNAME,PX.PAX_TYPE
FROM PAX PX WHERE PX.BOOKING_ID=:NEW.BOOKING_ID;
-- Insert into SECTOR_STAGE table
INSERT INTO SECTOR_STAGE (emco_document_number,ps_id,sector_seq,pax_seq,fare_class,fare_basis,net_fare,tax,gross_fare,
flight_number,departure_date,dep_airport_code,dest_airport_code)
SELECT :NEW.EMCO_DOCUMENT_NUMBER, PS.PS_ID,PS.SECTOR_SEQ,PS.PAX_SEQ,PS.FARE_CLASS,PS.FARE_BASIS,PS.NET_FARE,PS.TAX,PS.GROSS_FARE,BS.FLIGHT_NUMBER,BS.DEPARTURE_DATE,
BS.DEP_AIRPORT_CODE,BS.DEST_AIRPORT_CODE
FROM PAX_SECTORS PS, BOOKING_SECTORS BS
WHERE PS.BOOKING_ID=:NEW.BOOKING_ID
AND BS.BOOKING_ID=PS.BOOKING_ID;
-- Insert into TAX_STAGE table
INSERT INTO TAX_STAGE (emco_docUment_number,PS_ID,TAX_SEQ,TAX_CODE,VALUE)
SELECT :NEW.EMCO_DOCUMENT_NUMBER, pt.PS_ID,TAX_SEQ,TAX_CODE,VALUE
FROM PAX_TAX pt,PAX_SECTORS ps
WHERE ps.BOOKING_ID=:OLD.booking_id and pt.PS_ID=ps.PS_ID;
-- Insert into CHARGES_STAGE table
insert into CHARGES_STAGE (emco_document_number,CHARGE_TYPE,CHARGE_AMOUNT)
select :NEW.EMCO_DOCUMENT_NUMBER, CHARGE_TYPE,VALUE FROM INVOICE_DETAILS IND
WHERE :NEW.INVOICE_ID=IND.INVOICE_ID;
END IF;
EXCEPTION
WHEN OTHERS THEN
ecode := SQLCODE|| ' '||sqlerrm ;
dbms_output.put_line(thisproc || ' - ' || ecode);
v_value := thisproc || ' - ' || ecode;
-- Insert into DOCUMENT_STATUS_STAGE ANY ERRORS DURING PROCESSING
insert into DOCUMENT_STATUS_STAGE (EMCO_DOCUMENT_NUMBER, STATUS,STATUS_DATE)
VALUES (:NEW.EMCO_DOCUMENT_NUMBER,v_value,sysdate);
update invoices set TRANSFER_STATUS='F' where invoice_id=:old.invoice_id;
END pop_stage;
The second trigger (also below) does a check on the payments table to see if the payment_complete date has been set.
If this has been set then we want to sum all the payments (based on invoice_id). once this sum has been made we want to compare this value to the value of the gross amount on the invoices table for that invoice. if they match (which shows the invoce has been fully paid) then we want to update the paid_date and set to sysdate.
create or replace
TRIGGER CHECK_INVOICE_STATUS AFTER
UPDATE OF "PAYMENT_COMPLETE" ON "PAYMENTS" FOR EACH ROW
declare
-- check if all the payments for that invoice have been received (completed)
-- if they are complete then sum all the payments for that invoice and compare to gross value on invoices
v_invoice_amount number;
v_gross_amount number;
thisproc CONSTANT VARCHAR2(80) := 'trap_errmesg for mco_transfer_status';
v_value varchar2(150);
BEGIN
IF :NEW.PAYMENT_COMPLETE is not null
THEN
-- We will sum all the payments for that invoice that have a value in payment_complete
select sum(amount) into v_invoice_amount from payments where payment_complete is not null
group by :OLD.INVOICE_ID;
-- We will also get the gross amount for the invoice to compare to the sum of the payments
select gross into v_gross_amount from invoices where
:OLD.INVOICE_ID = invoice_id;
END IF;
IF v_invoice_amount = v_gross_amount
then
update invoices set paid_date=sysdate;
end if;
end CHECK_INVOICE_STATUS;
Is this trigger sufficent?
Any tips will be appreciate.
ThanksI didn't really look into your post (rather long) ... anyway, I have 2 words for you "stored procedures".
-
Mutating Trigger error while updating table
Hi Guys,
I am updating one table and after trigger also fire at the same time. Now, I want to avoid mutating trigger error. Can any one help me on this.
Thanks in advance!
Regards,
-LRKYou'll have to read these articles first, they explain what's the problem and how to deal with it:
http://www.oracle-base.com/articles/9i/MutatingTableExceptions.php
http://asktom.oracle.com/pls/asktom/ASKTOM.download_file?p_file=6551198119097816936
but, as Saubhik already said, using Oracle's AUDIT functionality would be the way to go.
http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_4007.htm
Using an autonomous transaction can result in corrupted data or unexpected errors, stay away from them if you want to use them in order to bypass/hide your mutating table error. -
ORA-04091 table string.string is mutating, trigger/function may not see it
When I am tending to delete something from my table I received this message.
I defined a PL/SQL function reads data from that table. Is that meaning I could not modify anything in table once I have some PL/SQL defined on that? It sounds ridiculour. Or I missed some points?
Anyone could help me out?
Many thanks,
QiangIn get_point_coordinates(point_id, layerid), it does not have any codes deleting thing from point_tab.
Its functionality is to find out that specified point;
put its coordinates into SDO_GEOMETRY object and return as function value.
However, oracle does not allow me to delete any data from point table since then.It is saying that function (get_point_coordinates) can not know mutating tables.
When I run delete task:
delete from point$_view where point_id = 1;
I got errors:
ERROR at line 1:
ORA-04091: table POINT$_TABLE is mutating, trigger/functio
n may not see it
ORA-06512: at "GET_POINT_COORDINATES", /* THIS LINE REPORS ERROR*/ Look at codes below.
The following is code of function: GET_POINT_COORDINATES
FUNCTION GET_POINT_COORDINATES(pPoint_ID IN NUMBER, player_ID IN NUMBER)
RETURN MDSYS.SDO_GEOMETRY DETERMINISTIC IS
PSRID NUMBER :=NULL:
PLON NUMBER := NULL;
PLAT NUMBER := NULL;
CURSOR get_lonlat(ppoint_id IN NUMBER, player_id IN NUMBER) IS SELECT LON,LAT
FROM POINT$_VIEW /* THIS LINE REPORS ERROR*/
WHERE player_id = layer_id AND ppoint_id = point_id;
BEGIN
PSRID := 8265;
OPEN get_lonlat(pPoint_ID,Player_ID);
FETCH get_lonlat INTO PLON, PLAT;
RETURN MDSYS.SDO_GEOMETRY(2001,PSRID,MDSYS.SDO_POINT_TYPE(PLON,PLAT,NULL),NULL ,NULL);
END GET_POINT_GEOM;
I guess it has nothing with foreign key. There is something wrong with this function.
By the way, a spatail index is built on this function. Does this make any differences on this point? -
ORA-04091: table ACCESSLOG is mutating, trigger/function may not see it
Hi
Got the following error
ORA-04091: table ACCESSLOG is mutating, trigger/function may not see it
i searched the error found that the problem is with FOR EACH ROW
how can i handel this specifically with the following code:
CREATE OR REPLACE TRIGGER EMP_ACCESS
AFTER INSERT
ON ACCESSLOG
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
tmpVar NUMBER;
BEGIN
IF ( :NEW.INOUT = 'OUT' ) THEN
SELECT 'X'
INTO tmpVar
FROM ACCESSLOG
WHERE EMPLOYEEID = :NEW.EMPLOYEEID
AND LOGDATE = :NEW.LOGDATE
AND INOUT ='IN' ;
IF tmpVar IS NULL THEN
INSERT INTO Emp_All_Moves_Absent (
EMP_NO,
ATT_DATE,
ATT_FLAG,
ATT_TIME_IN,
ATT_TIME_OUT,
VAC_COD,
MIS_CODE,
DAY_FLAG ,
POSTEDFLAG,
ATT_TIME_IN_PLANNED,ATT_TIME_OUT_PLANNED)
VALUES
(to_number(to_char(:NEW.employeeid,99999)),
TO_DATE(:NEW.LOGDATE,'DD/MM/YYYY'),
'ABS' ,
to_date(:NEW.LOGTIME,'HH24:MI:SS'),
to_date(:NEW.LOGTIME,'HH24:MI:SS'),
NULL,
NULL,
'WORK',
0,
NULL,NULL);
END IF;
ELSIF ( :NEW.INOUT = 'IN' ) THEN
SELECT 'X'
INTO tmpVar
FROM ACCESSLOG
WHERE EMPLOYEEID = :NEW.EMPLOYEEID
AND LOGDATE = :NEW.LOGDATE-1
AND INOUT ='IN' ;
IF tmpVar IS NULL THEN
INSERT INTO Emp_All_Moves_Absent (
EMP_NO,
ATT_DATE,
ATT_FLAG,
ATT_TIME_IN,
ATT_TIME_OUT,
VAC_COD,
MIS_CODE,
DAY_FLAG ,
POSTEDFLAG,
ATT_TIME_IN_PLANNED,ATT_TIME_OUT_PLANNED)
VALUES
(to_number(to_char(:NEW.employeeid,99999)),
TO_DATE(:NEW.LOGDATE-1,'DD/MM/YYYY'),
'ABS' ,
to_date(:NEW.LOGTIME,'HH24:MI:SS'),
to_date(:NEW.LOGTIME,'HH24:MI:SS'),
NULL,
NULL,
'WORK',
0,
NULL,NULL);
END IF;
END IF;
EXCEPTION
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
RAISE;
END EMP_ACCESS_LOG_OUT;
Urgent help is highly appreciated..
Regards,
Abdetu..Thanks Jens Petersen for reply
now i want to make sure of one thing that i have to follow the steps but the last one i will replace it with my trigger NO?
SQL> create or replace package state_pkg
2 as
3 type ridArray is table of rowid index by binary_integer;
4
4 newRows ridArray;
5 empty ridArray;
6 end;
7 /
Package created.
SQL> create or replace trigger parent_bi
2 before insert or update on parent
3 begin
4 state_pkg.newRows := state_pkg.empty;
5 end;
6 /
Trigger created.
SQL> create or replace trigger parent_aifer
2 after insert or update of status on parent for each row
3 begin
4 state_pkg.newRows( state_pkg.newRows.count+1 ) := :new.rowid;
5 end;
6 /
Trigger created.
---------------------now come my trigger instead of the following one ??---------------
SQL> create or replace trigger parent_ai
2 after insert or update of status on parent
3 begin
4 for i in 1 .. state_pkg.newRows.count loop
5 insert into log_table
6 select theKey, status, effDate
7 from parent where rowid = state_pkg.newRows(i);
8 end loop;
9 end;
10 /
Trigger created.
Regards,
Abdetu..
Maybe you are looking for
-
XPpro with 2.4ghz cpu, 2 gig ram, 2 much harddrive, single-user desktop, and I am the registered administrator. Problem is Adobe AIR will NOT install. I downloaded Adobe AIR installer and ran in normal way. Adobe AIR attempts to install and then crap
-
MAIL on my MAC does not load any new mail or delete any mail in any of my other folders
I've been having this problem for a couple of days now. Its as if MAIL just doesn't respond to anything. I can't get any New MAIL to load. And if i try to delete anything from SPAM, JUNK... and so on. They won't delete, they just show up again after
-
How to change from Mac OS X to Mac OS X Server
Hello everyone? I have a Mac Pro Late 2013 with Mac OS X v10.9.4. I want to change from Mac OS X to Mac OS X Server v10.9.4. Is it possible?
-
I have a new USB flash drive and when I plug it in it doesn't show up on the desk top. Any suggestions?
-
XFCE - Taskbar / Clock / "Window Buttons" plugin rotated by 90 degrees
Hello all, Back then I solved somehow on XFCE 4.10 that Window buttons were showed properly, not rotated by 90 degrees on XFCE when I put the panel on the right side. Now I can't find it how did I do it and also can't find anything on Google. Any ide