Is there a way to commit only the data inserted/updated on a table through a database link and not the data of the current session? Or are they considered one and the same?
For example:
INSERT INTO main_database.main_table(value1, value2)
VALUES (1 , 2)
INSERT INTO database.table#database_link(value3, value4)
VALUES (3 , 4)
And do a commit for only the database link table?
Background on why I would want to do this:
The main database is for (multiple) records while the database link is for (monetary) transactions (processed on a separate server). I want to update the records first to check to see if any of the constraints fail, but not commit the data until the transaction is complete. If the transaction fails, I want to rollback the records to save me the effort of deleting/undoing inserts/updates which could get messy.
I am assuming there is not but I am hoping that there is a way. Thanks in advance.
create or replace procedure proc_1 ( i IN number )
as
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO test_table#remote_sid (id, description)
VALUES (i, 'Description for ' || i);
COMMIT;
END;
/
create or replace procedure proc_base ( i IN number )
as
begin
insert into local_tab (id) values (i);
proc_1( i );
rollback;
end;
/
No.
If the insert is in a pl/SQL then you can run the first insert in an autonomous transaction, but in the absence of other functionality that's little different than insert....; commit; insert...
Related
How do I count page hit or no of visitor in oracle form d2k application?
Here's one option:
create a table, e.g.
create table hits (form_name varchar2(30), hits number);
create a stored procedure which will be used to maintain that table. It'll be an autonomous transaction procedure (so that you could commit without affecting the main transaction), and it'll lock the hits table (will it affect execution in multi-user environment? Shouldn't have, it is really fast).
create or replace procedure p_hits (par_form_name in varchar2)
is
pragma autonomous_transaction;
begin
lock table hits in exclusive mode;
merge into hits h
using (select par_form_name form_name from dual) x
on (x.form_name = h.form_name)
when matched then
update set h.hits = h.hits + 1
when not matched then
insert (form_name, hits) values (par_form_name, 1);
commit;
end p_hits;
/
call that procedure in form's WHEN-NEW-FORM-INSTANCE form-level trigger:
p_hits(:system.current_form);
That would be all; query the hits table to see its contents.
I get an error when working with the following trigger:
create or replace trigger t1
after insert or update
on student_tbl
declare
pragma autonomous_transaction;
begin
if inserting then
insert into stud_fees_details(stud_id,fees_balance,total_fees)
select stud_id,course_fees,course_fees from student_tbl s,courses_tbl c where s.stud_standard_id=c.course_id;
elsif updating('stud_standard_id') then
insert into stud_fees_details(stud_id,fees_balance,total_fees)
select stud_id,course_fees,course_fees from student_tbl s,courses_tbl c where s.stud_standard_id=c.course_id;
end if;
end;
error is
ORA-06519: active autonomous transaction detected and rolled back
ORA-06512: at "SYSTEM.T1", line 15
ORA-04088: error during execution of trigger 'SYSTEM.T1'
Database Error Messages
ORA-06519: active autonomous transaction detected and rolled back
Cause: Before returning from an autonomous PL/SQL block, all autonomous transactions started within the block must be completed (either committed or rolled back). If not, the active autonomous transaction is implicitly rolled back and this error is raised.
Action: Ensure that before returning from an autonomous PL/SQL block, any active autonomous transactions are explicitly committed or rolled back.
Example from Database PL/SQL Language Reference
-- Autonomous trigger on emp table:
CREATE OR REPLACE TRIGGER log_sal
BEFORE UPDATE OF salary ON emp FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO log (
log_id,
up_date,
new_sal,
old_sal
)
VALUES (
:old.employee_id,
SYSDATE,
:new.salary,
:old.salary
);
COMMIT;
END;
/
But #a_horse_with_no_name already stated that an autonomous transaction maybe is is not appropriate here.
After removing the autonomous transaction pragma you maybe will run into the problem that #GordonLinoff adresses with his post.
If a trigger doesn't use :new or :old, then it is suspicious. Your trigger is using the same table being modified in queries.
You probably intend:
create or replace trigger t1 after insert or update on student_tbl
declare
pragma autonomous_transaction;
begin
if inserting then
insert into stud_fees_details(stud_id, fees_balance, total_fees)
select stud_id, course_fees, course_fees
from courses_tbl c
where c.course_id = :new.stud_standard_id;
elsif updating('stud_standard_id') then
insert into stud_fees_details(stud_id, fees_balance, total_fees)
select stud_id, course_fees, course_fees
from courses_tbl c
where c.course_id = :new.stud_standard_id;
end if;
commit;
end;
Notes:
The select statements should have table aliases, to distinguish the columns that come from :new and from courses_tbl.
The two clauses to the if look the same to me, so I don't understand the logic.
The join conditions between something called course_id and somethign called stud_standard_id looks suspicious. I would advise you to name foreign keys after the primary key they refer to.
I'm wondering if I will miss any data if I replace a trigger while my oracle database is in use. I created a toy example and it seems like I won't, but one of my coworkers claims otherwise.
create table test_trigger (id number);
create table test_trigger_h (id number);
create sequence test_trigger_seq;
--/
create or replace trigger test_trigger_t after insert on test_trigger for each row
begin
insert into test_trigger_h (id) values (:new.id);
end;
/
--/
begin
for i in 1..100000 loop
insert into test_trigger (id) values (test_trigger_seq.nextval);
end loop;
end;
/
--/
begin
for i in 1..10000 loop
execute immediate 'create or replace trigger test_trigger_t after insert on test_trigger for each row begin insert into test_trigger_h (id) values (:new.id); end;';
end loop;
end;
/
ran the two loops at the same time
select count(1) from test_trigger;
COUNT(1)
100000
select count(1) from test_trigger_h;
COUNT(1)
100000
create or replace is locking the table. So all the inserts will wait until it completes. Don't worry about missed inserts.
I think you might be going about testing this in the wrong way. Your insert statements won't take any time at all and so the replacement of the trigger can fit in through the gaps between inserts. As least this is what I infer due to the below.
If you change your test to ensure you have a long running SQL statement, e.g.
create table test_trigger (id number);
create table test_trigger_h (id number);
create sequence test_trigger_seq;
create or replace trigger test_trigger_t
after insert on test_trigger for each row
begin
insert into test_trigger_h (id) values (:new.id);
end;
/
insert into test_trigger
select level
from dual
connect by level <= 1000000;
If you then try to replace the trigger in a separate session it will not occur until after the insert has completed.
Unfortunately, I can't find anything in the documentation to back me up; this is just behavior that I'm aware of.
Following URL answers that trigger can be modified while application is running. its will a "library cache" lock and NOT a "data" lock. Oracle handles it internally without you worrying abt it.
Check out question raised by Ben- Can a trigger be locked; how would one determine that it is?
-- Run this from session 2:
select * from v$access where object = upper('test_trigger_t');
I'm very new for trigger, now this what i was trying. I've two tables INSERTED and ORDER_INFO, both have the same column name ORDER_ID, ORDER_DATE. I've scenario, where client will be placing his/her order then, order information will be stored into INSERTED table, then by using this trigger, it'll insert into another table ORDER_INFO after satisfying the condition, which has been written.
create trigger tri_check
AFTER INSERT ON inserted FOR EACH ROW
DECLARE
v_date DATE;
BEGIN
SELECT order_date INTO v_date FROM inserted;
if (v_date)< (sysdate + 2) then
raiserror('You cannot take an order to be delivered less than 2 days from now',16, 1);
else
INSERT INTO orders_info
( order_id,order_date)
VALUES
(:new.order_id,v_date);
end if;
end;
But, when i'm executing the above trigger, then i'm getting this error.
ERROR at line 8: PL/SQL: SQL Statement ignored
6. SELECT order_date INTO v_date FROM inserted;
7. if (v_date)< (sysdate + 2) then
8. raiserror('You cannot take an order to be delivered less than 2 days from now',16, 1);
9. else
10. INSERT INTO orders_info
EDIT
Now, i made the same structure table into SYSTEM user, and got the same error. Table or View does not exist
Need help !! Thanks in advance !!
The message seems to indicate a problem with the 'raiserror' procedure. I'm not familiar with such a procedure in standard PL/SQL - did you mean RAISE_APPLICATION_ERROR? However, and perhaps more to the point, when using a trigger there's no need to do a SELECT from the table. All the data being inserted is available to the trigger. I suggest changing your trigger to be something like the following:
create trigger tri_check
AFTER INSERT ON inserted
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
if :new.ORDER_DATE < sysdate + INTERVAL '2' DAY then
RAISE_APPLICATION_ERROR(-20000, 'You cannot take an order to be delivered less than 2 days from now');
else
INSERT INTO orders_info
(order_id, order_date)
VALUES
(:new.order_id, :new.ORDER_DATE);
end if;
end TRI_CHECK;
Share and enjoy.
You can just use the :NEW and :OLD values instead of your select:
CREATE TRIGGER tri_check
AFTER INSERT
ON inserted
FOR EACH ROW
DECLARE
BEGIN
IF :new.order_date < (SYSDATE + 2)
THEN
raiserror (
'You cannot take an order to be delivered less than 2 days from now',
16,
1);
ELSE
INSERT INTO orders_info (order_id, order_date)
VALUES (:new.order_id, :new.order_date);
END IF;
END;
What is your raiserror procedure? Do you have access permissions granted on it?
Hope it helps...
EDIT:
OK, from your error, and the error you posted on #Bob Jarvis' answer, you might not have INSERT privilege on the ORDERS_INFO table. You also should check your permissions on the INSERTED table too.
Check your permissions with your DBA.
If raiserror is not a defined procedure or you don't have access to it then use the RAISE_APPLICATION_ERROR method for raising an error as Bob suggests.
what is the best solution to write a oracle package for record persistence?
I've always written something like this:
create or replace
PACKAGE BODY "USP_PRICELIST" AS
PROCEDURE usp_TABLE1Save
(
pErrorCode OUT NUMBER,
pMessage OUT VARCHAR2,
pPARAM1 IN CHAR,
pPARAM2 IN CHAR
)
IS
BEGIN
pErrorCode := 0;
INSERT INTO TABLE1
(PARAM1, PARAM2)
VALUES
(pPARAM1, pPARAM2);
EXCEPTION
WHEN OTHERS THEN pErrorCode := SQLCODE; pMessage := SQLERRM;
END usp_TABLE1Save;
END USP_PRICELIST;
and I was wondering if I have to COMMIT after the INSERT INTO.
Alberto
I would not put a commit in the procedure, and leave that to the code that calls the procedure. This allows the procedure to be used as part of a larger transaction. The insert is not implicitly committed.
It really depends on whether you want your operation to take part in a transaction or to be atomic.
Be careful, if you place the commit in the package it will commit the entire transaction
create table testcommit (colA varchar2(50)) ;
DECLARE
PROCEDURE SELFCOMMIT(VAL IN TESTCOMMIT.COLA%TYPE) AS
BEGIN
INSERT INTO TESTCOMMIT(COLA) VALUES(VAL);
COMMIT ;
END SELFCOMMIT ;
PROCEDURE NOCOMMIT(VAL IN TESTCOMMIT.COLA%TYPE) AS
BEGIN
INSERT INTO TESTCOMMIT(COLA) VALUES(VAL);
END NOCOMMIT ;
BEGIN
INSERT INTO TESTCOMMIT(COLA) VALUES('INITIAL');
SELFCOMMIT('FIRST SELF COMMIT');
ROLLBACK ; --KILL TRANSACTION
INSERT INTO TESTCOMMIT(COLA) VALUES('SECOND MAIN INSERT');
NOCOMMIT('NO AUTO COMMIT');
ROLLBACK;
END ;
/
SELECT * FROM TESTCOMMIT;
-->
COLA
--------------------------------------------------
INITIAL
FIRST SELF COMMIT
-->NOTE THE SELFCOMMIT AFFECTS THE ENTIRE TRANSACTION, THUS RENDERING THE ROLLBACK MOOT
--drop table testcommit;
You should also look at the concept of autonomous transactions
By default Oracle has no auto-commit, so you have to.