8 Feb 2015

ORA_ROWSCN: The pseudo Column

ORA_ROWSCN is a pseudocolumn of any table which has the most recent change information to a given row.

Oracle has an ORA_ROWSCN pseudocolumn which reports the last known change time for a row in a table. The “time” shows a commit SCN number of last transaction modifying the row, not a real timestamp though. It is important to note that unless the ROWDEPENDECIES are enabled, then the last SCN is known only at data block level, not row level, rowscn’s for all rows in a block would report whatever SCN is in the last change SCN in block header.
ORA_ROWSCN and SCN_TO_TIMESTAMP. Using this ORA_ROWSCN column and SCN_TO_TIMESTAMP function, the last date or timestamp can be found when a table or record updated.

Here is an example to get the ORA_ROWSCN value when a row updated.

For each row, ORA_ROWSCN returns the conservative upper bound system change number (SCN) of the most recent change to the row. This pseudocolumn is useful for determining approximately when a row was last updated. It is not absolutely precise, because Oracle tracks SCNs by transaction committed for the block in which the row resides.
hum…”not absolutely precise”. Should I understand “absolutely not precise” ?
Let’s see that.
create table test(col1 varchar2(10));

insert into test values('1');
insert into test values('2');

select ora_rowscn, col1 from test;

ORA_ROWSCN COL1
---------- ----------
9.8420E+12 1
Let’s try to have a better display:
select  to_char(cast(scn_to_timestamp(ora_rowscn) as date),
          'DD/MM/YYYY HH24:MI:SS') ora_rowscn_date,
        col1
from    test;

ORA_ROWSCN_DATE     COL1
------------------- ----------
02/08/2013 16:41:30 1
02/08/2013 16:41:30 2
Ok. something different if I commit?
ORA_ROWSCN_DATE     COL1
------------------- ----------
02/08/2013 16:43:30 1
02/08/2013 16:43:30 2
Yes. ORA_ROWSCN is set to insertion SCN first, then to commit SCN.
What about inserting a new value?
insert into test values('3');

ORA_ROWSCN_DATE     COL1
------------------- ----------
02/08/2013 16:43:30 1
02/08/2013 16:43:30 2
02/08/2013 16:43:30 3
Ha? Funny, may last insertion SCN is the same as the last commited one. Seems to be copied.
Ok. something different if I commit?
ORA_ROWSCN_DATE     COL1
------------------- ----------
02/08/2013 16:46:48 1
02/08/2013 16:46:48 2
02/08/2013 16:46:48 3
All records are now updated with the latest commit SCN.
Looking at the extract of the doc above, I understand this is because all the records are stored into the same block. Let’s check that:
select  to_char(cast(scn_to_timestamp(ora_rowscn) as date),
          'DD/MM/YYYY HH24:MI:SS') ora_rowscn_date,
        col1,
        dbms_rowid.rowid_block_number(rowid) block_number
from    test;

ORA_ROWSCN_DATE     COL1       BLOCK_NUMBER
------------------- ---------- ------------
02/08/2013 16:46:48 1                 90625
02/08/2013 16:46:48 2                 90625
02/08/2013 16:46:48 3                 90625
That’s true (it was obvious with so little number of record :) ).
Let’s see if we have records scatterred into different blocks:
create table test2 as select rownum col1 from dba_objects where rownum <=3000;

col ora_rowscn_date for a25      
select  dbms_rowid.rowid_block_number(rowid) block_number,
        to_char(cast(scn_to_timestamp(ora_rowscn) as date),
          'DD/MM/YYYY HH24:MI:SS') ora_rowscn_date,
        count(*)
from    test2
group by
        dbms_rowid.rowid_block_number(rowid),
        to_char(cast(scn_to_timestamp(ora_rowscn) as date),
          'DD/MM/YYYY HH24:MI:SS')
;  

BLOCK_NUMBER ORA_ROWSCN_DATE             COUNT(*)
------------ ------------------------- ----------
       47481 05/08/2013 15:38:22              658
       47484 05/08/2013 15:38:22              658
       47485 05/08/2013 15:38:22              368
       47482 05/08/2013 15:38:22              658
       47483 05/08/2013 15:38:22              658
What about inserting a new value?
insert into test2 values (999);

BLOCK_NUMBER ORA_ROWSCN_DATE             COUNT(*)
------------ ------------------------- ----------
       47481 05/08/2013 15:38:22              658
       47484 05/08/2013 15:38:22              658
       47485 05/08/2013 15:38:22              368
       47486 05/08/2013 15:41:42                1
       47482 05/08/2013 15:38:22              658
       47483 05/08/2013 15:38:22              658
I can see my new value stored into a new block whith insertion SCN.
What happen if I commit?
BLOCK_NUMBER ORA_ROWSCN_DATE             COUNT(*)
------------ ------------------------- ----------
       47481 05/08/2013 15:38:22              658
       47484 05/08/2013 15:38:22              658
       47485 05/08/2013 15:38:22              368
       47482 05/08/2013 15:38:22              658
       47483 05/08/2013 15:38:22              658
       47486 05/08/2013 15:42:18                1
I get the commit SCN for my record.
What about inserting another new record?
insert into test2 values (1000);

BLOCK_NUMBER ORA_ROWSCN_DATE             COUNT(*)
------------ ------------------------- ----------
       47481 05/08/2013 15:38:22              658
       47484 05/08/2013 15:38:22              658
       47485 05/08/2013 15:38:22              368
       47482 05/08/2013 15:38:22              658
       47483 05/08/2013 15:38:22              658
       47486 05/08/2013 15:42:18                2
I can see my second new record stored into the same block as the first one. The SCN did not change.
What happen if I Commit?
BLOCK_NUMBER ORA_ROWSCN_DATE             COUNT(*)
------------ ------------------------- ----------
       47481 05/08/2013 15:38:22              658
       47484 05/08/2013 15:38:22              658
       47485 05/08/2013 15:38:22              368
       47482 05/08/2013 15:38:22              658
       47483 05/08/2013 15:38:22              658
       47486 05/08/2013 15:43:56                2
I get the commit SCN for my two new records in the same block.
What about updating an existing record?
update test2 set col1=9999 where col1=1;   

BLOCK_NUMBER ORA_ROWSCN_DATE             COUNT(*)
------------ ------------------------- ----------
       47481 05/08/2013 15:38:22              658
       47484 05/08/2013 15:38:22              658
       47485 05/08/2013 15:38:22              368
       47482 05/08/2013 15:38:22              658
       47483 05/08/2013 15:38:22              658
       47486 05/08/2013 15:43:56                2
Nothing happened, no new SCN. Now I commit.
BLOCK_NUMBER ORA_ROWSCN_DATE             COUNT(*)
------------ ------------------------- ----------
       47481 05/08/2013 15:45:41              658
       47484 05/08/2013 15:38:22              658
       47485 05/08/2013 15:38:22              368
       47482 05/08/2013 15:38:22              658
       47483 05/08/2013 15:38:22              658
       47486 05/08/2013 15:43:56                2                            
I can see my commit SCN for all the records in the same block as the record I updated.
This clearly shows that the ora_rowscn is tracked at block level.
When inserting data, all the inserted records get the same SCN as the first inserted record, when they go into the same block(s), until commit.
When commit happens, all the records in the block(s) where data has been inserted get the same commit SCN.
When updating data, all the updated records do not get their SCN updated, until commit command is ran.
When commit happens, all the records in the same block as the updated record get the commit SCN.


SCN_TO_TIMESTAMP

SCN_TO_TIMESTAMP is a new function, in Oracle 10g, which is used to convert the SCN value generated, using ORA_ROWSCN coumn, into timestamp. SCN_TO_TIMESTAMP takes as an argument a number that evaluates to a system change number (SCN), and returns the approximate timestamp associated with that SCN. The returned value is of TIMESTAMP datatype. This function is useful any time you want to know the timestamp associated with an SCN.

Here we pass the scn value generated in the above query.

SELECT scn_to_timestamp(353845494) FROM emp WHERE empno=7839;

SCN_TO_TIMESTAMP(353845494)
------------------------------------------------
02-SEP-08 03.20.20.000000000 PM

SCN_TO_TIMESTAMP function can also be used in conjunction with the ORA_ROWSCN pseudocolumn to associate a timestamp with the most recent change to a row.

SELECT scn_to_timestamp(ORA_ROWSCN) FROM emp WHERE empno=7839;

SCN_TO_TIMESTAMP(ORA_ROWSCN)
-------------------------------------------------------
02-SEP-08 03.20.20.000000000 PM

No comments:

Post a Comment