Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Manish.

Asked: April 28, 2008 - 7:50 am UTC

Last updated: April 29, 2008 - 10:32 am UTC

Version: 9.2.0.4.0

Viewed 1000+ times

You Asked

Dear Tom,

See updated scenario.

I have a very strange scenario. I have one table which gets populated by SQL Loader on contineous basis. This table doesn't have any unique column and hence which data came on what date is very difficult to find out. This table is populating thoughout the month and I have to capture daily inserted data for that only. This table structure can not be changed because this is third party application table. Hence I have created hourly basis partition table which will hold similar record of original record along with original rowid. To populate this partition table I have created simple row level trigger on original table which inserts the record along with rowid. Upon investigation it is found that, partition table is having duplicate rowid of original table but corrosponding records in partition table are different. See following senario.

I have two tables say emp1 and emp2. I am inserting data in emp1 table and I have a insert trigger on it
Through the insert trigger i am inserting data into emp2 table with sysdate and rowid of emp1 table.
when iam inserting 2 rows into emp1 table it has to insert 2rows rows in emp2 table.
But here my problem is it is inseting 3 rows into emp2 table ,but the extra third row is taking rowid of first row
and data is of 2nd row.

Please see the bleow example

Table 1:

create table Emp1
(eno number
ename varchar2(100))

Table 2:


create table Emp2
(eno number
ename varchar2(100)
sys_date timestamp
row_id varchar2(50))
NOLOGGING
PARTITION BY RANGE (SYS_DATE)
(PARTITION P_20080402_00 VALUES LESS THAN (TIMESTAMP'2008-04-01 01:00:00')),
(PARTITION P_20080402_00 VALUES LESS THAN (TIMESTAMP'2008-04-01 02:00:00')),
(PARTITION P_20080402_00 VALUES LESS THAN (TIMESTAMP'2008-04-01 03:00:00')),
(PARTITION P_20080402_00 VALUES LESS THAN (TIMESTAMP'2008-04-01 04:00:00')),
(PARTITION P_20080402_00 VALUES LESS THAN (TIMESTAMP'2008-04-01 05:00:00')),
.
.
.
(PARTITION P_20080402_00 VALUES LESS THAN (TIMESTAMP'2008-04-01 23:00:00')),
(PARTITION P_20080402_00 VALUES LESS THAN (TIMESTAMP'2008-04-02 00:00:00'))


Trigger
=======
create or replace trigger emp_trig after insert or delete on emp1 foreachrow
declare
pragma atonomous_transaction;

begin
insert into emp2 values(:new.eno,:new.ename,sysdate,:new.rowid);
commit;
end;


Two records inserted in EMP1
ENO ENAME EMP1_ROWID
101 ABC AAHPpyABOAAAO+PAAA
102 CDE AAHPpyABRAAAkDeAAu


Records in EMP2 table inserted due to trigger
ENO ENAME SYS_DATE EMP1_ROWID
101 ABC 12-APR-08 08.36.54.000000 PM AAHPpyABOAAAO+PAAA
102 CDE 13-APR-08 06.23.54.000000 AM AAHPpyABRAAAkDeAAu
102 CDE 12-APR-08 04.50.35.000000 PM AAHPpyABOAAAO+PAAA DUPLICATE




Regards

Manish Bhavsar

and Tom said...

OH MY GOSH

why do you have an autonomous transaction!!!!!!!!!!!!!!!!!!

what was the thought process behind that???

also, if you have an "after INSERT OR DELETE" trigger and you only reference :new.rowid.... hmmm think about that....


I can give you three reasons for these duplicates.

1) first, if you insert and DELETE records - we'll definitely reuse rowids. rowids are unique within a table - period. We reuse them over time - absolutely. So if you delete from emp1, you are absolutely going to get "duplicates". We have to assume you do delete since - well - this is an after delete trigger..


2) second, you are doing something NON-TRANSACTIONAL in that trigger. If the triggering statement rolls back - guess what? You will have inserted the row into emp2 and committed it. IT WILL NOT ROLL BACK. The roll back will be just like a delete on emp1. So, you are back to reason #1 - you delete (everyone deletes - no one, NO ONE can say "we do not delete", if you insert - I am 100% sure you DELETE - because you rollback sometimes!!!!!!!!!!)


3) you assume we fire your trigger once for each row. Maybe we do, maybe we don't (hint: sometimes we don't, sometimes we fire it TWICE - we are allowed to, it has always been that way). This is why doing non-transactional things in a trigger (autonomous transactions, utl_file, utl_http, etc) is extremely dangerous.

http://asktom.oracle.com/Misc/something-different-part-i-of-iii.html
http://asktom.oracle.com/Misc/part-ii-seeing-restart.html
http://asktom.oracle.com/Misc/part-iii-why-is-restart-important-to.html




I'm going to guess #2 is your culprit, your application attempts to create employee X - something failed - got their address wrong or something and the application rolls back. The end user fixes the error and tries again - and there you go.... reused rowid...


Your use of rowid as a unique key is flawed, it'll never work long term - you'll need to find another approach.


LOSE THE PRAGMA - absolutely - it is a really bad use of this 'feature', a 'feature' I wish I could get removed from the database since it is almost always used *wrong* like this.

(yes, everyone that uses it right, I know you do - I don't care - 99.9999% of the usages of this 'feature' are wrong and dangerous and misunderstood. I would rather annoy the 0.0001% of people that know when and how to use it for the safety of everything else - like our data)

Is this answer out of date? If it is, please let us know via a Comment

More to Explore

VLDB

If you are new to partitioning, check out Connor McDonald's introduction series here.

VLDB

Documentation set on VLDB and Partitioning.