Home>Question Details



Mahmood -- Thanks for the question regarding "ORA_ROWSCN strange behavior", version 10.2.0

Submitted on 29-Oct-2007 22:08 Central time zone
Last updated 26-Aug-2009 19:18

You Asked

Hello Tom,

Lucky enough to get to submit a new question..

On ORA_ROWSCN, when I update a row, it is expected that ORA_ROWSCN number for that particular row is changed. But I noticed this strange behavior...Could you please explain this..

Here is what I had tried:

Thanks in advance for your Help!!

SCOTT@MYDB> select ora_rowscn,empno,ename from emp;

ORA_ROWSCN      EMPNO ENAME
---------- ---------- ----------
   4443685       7369 SMITH
   4443685       7499 ALLEN
   4443685       7521 WARD
   4443685       7566 JONES
   4443685       7654 MARTIN
   4443685       7698 BLAKE
   4443685       7782 CLARK
   4443685       7788 SCOTT
   4443685       7839 KING
   4443685       7844 TURNER
   4443685       7876 ADAMS
   4443685       7900 JAMES
   4443685       7902 FORD
   4443685       7934 MILLER

SCOTT@MYDB> update emp set ename=lower(ename) where empno=7369;

SCOTT@MYDB> commit;

SCOTT@MYDB> select ora_rowscn,empno,ename from emp;

ORA_ROWSCN      EMPNO ENAME
---------- ---------- ----------
   4494942       7369 smith
   4494942       7499 ALLEN
   4494942       7521 WARD
   4494942       7566 JONES
   4494942       7654 MARTIN
   4494942       7698 BLAKE
   4494942       7782 CLARK
   4494942       7788 SCOTT
   4494942       7839 KING
   4494942       7844 TURNER
   4494942       7876 ADAMS
   4494942       7900 JAMES
   4494942       7902 FORD
   4494942       7934 MILLER

Here ORA_ROWSCN number of first row only should have been changed? Isn't it?
Why is it doing for all?

and we said...

this is the expected *default* behavior. Here is an excerpt from Expert Oracle Database Architecture that describes what is happening in more detail:

<quote>
Optimistic Locking Using ORA_ROWSCN

Starting with Oracle 10g Release 1, you have the option to use the built-in ORA_ROWSCN function. It works very much like the version column technique described previously, but it can be performed automatically by Oracle¿you need no extra column in the table and no extra update/maintenance code to update this value.

ORA_ROWSCN is based on the internal Oracle system clock, the SCN. Every time you commit in Oracle, the SCN advances (other things can advance it as well, but it only advances; it never goes back). The concept is identical to the previous methods in that you retrieve ORA_ROWSCN upon data retrieval, and you verify it has not changed when you go to update. The only reason I give it more than passing mention is that unless you created the table to support the maintenance of ORA_ROWSCN at the row level, it is maintained at the block level. That is, by default many rows on a single block will share the same ORA_ROWSCN value. If you update a row on a block with 50 other rows, then they will all have their ORA_ROWSCN advanced as well. This would almost certainly lead to many false positives, whereby you believe a row was modified that in fact was not. Therefore, you need to be aware of this fact and understand how to change the behavior.

To see the behavior and then change it, we¿ll use the small DEPT table again:

ops$tkyte@ORA10G> create table dept
  2  (deptno, dname, loc, data,
  3   constraint dept_pk primary key(deptno)
  4  )
  5  as
  6  select deptno, dname, loc, rpad('*',3500,'*')
  7    from scott.dept;
Table created. 


Now we can inspect what block each row is on (it is safe to assume in this case they are in the same file, so a common block number indicates they are on the same block). I was using an 8KB block size with a row width of about 3,550 bytes, so I am expecting there to be two rows per block for this example:


ops$tkyte@ORA10G> select deptno, dname,
  2         dbms_rowid.rowid_block_number(rowid) blockno,
  3             ora_rowscn
  4    from dept;
 
    DEPTNO DNAME             BLOCKNO ORA_ROWSCN
---------- -------------- ---------- ----------
        10 ACCOUNTING          20972   34676029
        20 RESEARCH            20972   34676029
        30 SALES               20973   34676029
        40 OPERATIONS          20973   34676029


And sure enough, that is what we observe in this case. So, let¿s update the row where DEPTNO = 10 on block 20972:

ops$tkyte@ORA10G> update dept
  2     set dname = lower(dname)
  3   where deptno = 10;
1 row updated.
 
ops$tkyte@ORA10G> commit;
Commit complete.


What we¿ll observe next shows the consequences of ORA_ROWSCN being tracked at the block level. We modified and committed the changes to a single row, but the ORA_ROWSCN values of both of the rows on block 20972 have been advanced:

ops$tkyte@ORA10G> select deptno, dname,
  2         dbms_rowid.rowid_block_number(rowid) blockno,
  3             ora_rowscn
  4    from dept;
 
    DEPTNO DNAME             BLOCKNO ORA_ROWSCN
---------- -------------- ---------- ----------
        10 accounting          20972   34676046
        20 RESEARCH            20972   34676046
        30 SALES               20973   34676029
        40 OPERATIONS          20973   34676029


It would appear to anyone else that had read the DEPTNO=20 row that it had been modified, even though it was not. The rows on block 20973 are 'safe'¿we didn¿t modify them, so they did not advance. However, if we were to update either of them, both would advance. So the question becomes how to modify this default behavior. Well, unfortunately, we have to re-create the segment with ROWDEPENDENCIES enabled.

Row dependency tracking was added to the database with Oracle9i in support of advanced replication, to allow for better parallel propagation of changes. Prior to Oracle 10g, its only use was in a replication environment, but starting in Oracle 10g we can use it to implement an effective optimistic locking technique with ORA_ROWSCN. It will add 6 bytes of overhead to each row (so it is not a space saver compared to the do-it-yourself version column) and that is, in fact, why it requires a table re-create and not just a simple ALTER TABLE: the physical block structure must be changed to accommodate this feature.

Let¿s rebuild our table to enable ROWDEPENDENCIES. We could use the online rebuild capabilities in DBMS_REDEFINITION (another supplied package) to do this, but for something so small, we¿ll just start over:

ops$tkyte@ORA10G> drop table dept;
Table dropped.
 
ops$tkyte@ORA10G> create table dept
  2  (deptno, dname, loc, data,
  3   constraint dept_pk primary key(deptno)
  4  )
  5  ROWDEPENDENCIES
  6  as
  7  select deptno, dname, loc, rpad('*',3500,'*')
  8    from scott.dept;
Table created.
 
ops$tkyte@ORA10G> select deptno, dname,
  2         dbms_rowid.rowid_block_number(rowid) blockno,
  3             ora_rowscn
  4    from dept;
 
    DEPTNO DNAME             BLOCKNO ORA_ROWSCN
---------- -------------- ---------- ----------
        10 ACCOUNTING          21020   34676364
        20 RESEARCH            21020   34676364
        30 SALES               21021   34676364
        40 OPERATIONS          21021   34676364


We¿re back where we were before: four rows on two blocks, all having the same initial ORA_ROWSCN value. Now when we update DEPTNO=10


ops$tkyte@ORA10G> update dept
  2     set dname = lower(dname)
  3   where deptno = 10;
1 row updated.
 
ops$tkyte@ORA10G> commit;
Commit complete.


we should observe the following upon querying the DEPT table:

ops$tkyte@ORA10G> select deptno, dname,
  2         dbms_rowid.rowid_block_number(rowid) blockno,
  3             ora_rowscn
  4    from dept;
 
    DEPTNO DNAME             BLOCKNO ORA_ROWSCN
---------- -------------- ---------- ----------
        10 accounting          21020   34676381
        20 RESEARCH            21020   34676364
        30 SALES               21021   34676364
        40 OPERATIONS          21021   34676364


The only modified ORA_ROWSCN at this point belongs to DEPTNO = 10, exactly what we wanted. We can now rely on ORA_ROWSCN to detect row-level changes for us.

</quote>
Reviews    
5 stars Convinced   October 30, 2007 - 3pm Central time zone
Reviewer: Mahmood Lebbai from Bothell, WA
Tom,

I myself experimented that and I got it why now it happens.
Thanks for the quoting from your book..Though I got all three of your books, I did not get to read 
the section you quoted from.

You are right on! Thanks for you response.


4 stars Excellent   October 31, 2007 - 12pm Central time zone
Reviewer: chaman 
Hello Tom,
Thanks for explaining but was wondering in what scenarios the ORA_ROWSCN can be used?


Followup   November 1, 2007 - 4pm Central time zone:

http://asktom.oracle.com/pls/ask/search?p_string=lost+update+ora_rowscn


to prevent lost updates
5 stars ora_rowscn number changed by hibernate   March 18, 2009 - 5pm Central time zone
Reviewer: A reader 
Hi Tom,

we are trying to implement ora_rowscn to do optimistic locking and we ran into a problem with 
Hibernate when we run updates statements, hibernates increments by 1 the ora_rowscn number that we 
originally got when we first select the rows, triggering the StaleObjectException.
The java developers have tried to resolve this, but they dont' see a way to make using ORA_ROWSCN 
work with the second-level cache in hibernate.  If they could tell Hibernate to invalidate the 
cache when updates are done instead of putting the updated entity directly into the cache, they 
think they could make it work.  Unfortunately they haven't been able to find a way to modify the 
cache's behavior.
Have you run into this problem with Hibernate?
Any help you can give us with this problem we will greatly appreciated.

Thanks a lot.


Followup   March 19, 2009 - 10am Central time zone:

... Have you run into this problem with Hibernate? ...

Nope, but only because I wouldn't be using it....

You'll need to go to the makers of hibernate.
5 stars ora_rowscn number changed by hibernate   March 20, 2009 - 11pm Central time zone
Reviewer: A reader 
Thanks for answering.
So, what would you use in a java development environment instead of hibernate?


Followup   March 24, 2009 - 10am Central time zone:

well, in the olden days, our programmers actually used SQL and took the time to learn the most important component of their application infrastructure - the thing that persisted long after their applications died and went away.

The database.....


What would I use in a <any language, java isn't special> environment?

Stored procedures for transactional API's - not TABLE api's, TRANSACTIONAL apis.

And they would not be coded by the java developers since they haven't read a book on databases yet. They would be developed by people that understand data integrity, locking, concurrency controls, sql, etc...
5 stars Really weird ORA_ROWSCN behaviour   April 3, 2009 - 7am Central time zone
Reviewer: Martin Vajsar from Prague, Czech Republic
Tom, could you please comment on the really weird results here? I wanted to see what happens to ora_rowscn in session 2 when session 1 has uncommited modifications in the table (rowdependencies on) and I seem to get garbage here. After commiting the changes it is all right again:

SQL> select * from v$version;

BANNER                                                                          
----------------------------------------------------------------                
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bi                
PL/SQL Release 10.2.0.1.0 - Production                                          
CORE    10.2.0.1.0    Production                                                      
TNS for 64-bit Windows: Version 10.2.0.1.0 - Production                         
NLSRTL Version 10.2.0.1.0 - Production                                          

SQL> -- *** SESSION 1 ***
SQL> create table a(id number, text char(2000)) rowdependencies;

Table created.

SQL> insert into a
  2  (select level, 'Level ' || level from dual connect by level <= 100);

100 rows created.

SQL> commit;

Commit complete.

SQL> update a set text = 'Nothing' where id <= 20;

20 rows updated.

SQL> -- *** No commit! ***

Session 2

SQL> -- *** SESSION 2: ***
SQL> select to_Char(ora_rowscn, '999,999,999,999,999,999'), count(*)
  2  from a group by ora_rowscn;

TO_CHAR(ORA_ROWSCN,'999,   COUNT(*)                                             
------------------------ ----------                                             
      35,322,350,018,592         12                                             
              21,408,754         88                                             

SQL> select scn_to_timestamp(ora_rowscn), count(*)
  2  from a group by ora_rowscn;
select scn_to_timestamp(ora_rowscn), count(*)
       *
ERROR at line 1:
ORA-08181: specified number is not a valid system change number 
ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1 

Session 1

SQL> -- *** SESSION 1 ***
SQL> commit;

Commit complete.

Session 2

SQL> -- *** SESSION 2: ***
SQL> select to_Char(ora_rowscn, '999,999,999,999,999,999'), count(*)
  2  from a group by ora_rowscn;

TO_CHAR(ORA_ROWSCN,'999,   COUNT(*)                                             
------------------------ ----------                                             
              21,408,796         20                                             
              21,408,754         80                                             

SQL> select scn_to_timestamp(ora_rowscn), count(*)
  2  from a group by ora_rowscn;

SCN_TO_TIMESTAMP(ORA_ROWSCN)                                                    
---------------------------------------------------------------------------     
  COUNT(*)                                                                      
----------                                                                      
03.04.09 13:28:27,000000000                                                     
        20                                                                      
                                                                                
03.04.09 13:28:16,000000000                                                     
        80                                                                      
                                                                                


Followup   April 3, 2009 - 9am Central time zone:

that is an odd one,

ops$tkyte%ORA10GR2> select to_char( 35322350018592, 'xxxxxxxxxxxxxxxxx' ) from dual;

TO_CHAR(3532235001
------------------
      202020202020




it is as if some of them were blanked out (literally 0x20 is ascii space)

I reproduced in 10gr2 (10.2.0.4)
I cannot reproduce in 10gr1 or 11gr1


ops$tkyte%ORA10GR2> create table a(id number, text char(2000)) rowdependencies;

Table created.

ops$tkyte%ORA10GR2> create or replace procedure p
  2  as
  3          pragma autonomous_transaction;
  4  begin
  5          for x in ( select to_Char(ora_rowscn, '999,999,999,999,999,999') ||
  6                            ', ' || to_char(min(id),'999') || ', ' ||
  7                                            to_char(max(id),'999') data, count(*) cnt
  8                       from a group by to_Char(ora_rowscn, '999,999,999,999,999,999') )
  9          loop
 10                  dbms_output.put_line( '-' || x.data || ' -- ' || x.cnt );
 11          end loop;
 12          commit;
 13  end;
 14  /

Procedure created.

ops$tkyte%ORA10GR2> insert into a (select level, 'Level ' || level from dual connect by level <= 
100);

100 rows created.

ops$tkyte%ORA10GR2> commit;

Commit complete.

ops$tkyte%ORA10GR2> exec p
-              82,521,998,    1,  100 -- 100

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> update a set text = 'Nothing' where id <= 20;

20 rows updated.

ops$tkyte%ORA10GR2> exec p
-              82,521,998,    4,  100 -- 88
-      35,322,350,018,592,    1,   15 -- 12

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> commit;

Commit complete.

ops$tkyte%ORA10GR2> exec p
-              82,522,016,    1,   20 -- 20
-              82,521,998,   21,  100 -- 80

PL/SQL procedure successfully completed.




what is strange is that it is not a contiguous set of rows. They are all contained in the first 15 rows (that funky number), but id = 4 for example has the old rowscn.


Looks confusing but I think rather harmless. I don't see any bugs filed for it as yet.
2 stars can rman copy of databse   May 7, 2009 - 5pm Central time zone
Reviewer: A reader 
with same dbid and dbname have same ora_rowscn.


Followup   May 11, 2009 - 1pm Central time zone:

it is called a "full restore", yes.
3 stars SCN_TO_TIMESTAMP(ORA_ROWSCN)   August 19, 2009 - 4am Central time zone
Reviewer: Sandeep from MH India
Hi I am trying to get the rowtimestamp of the table while doing it in my machine on my personal 
database it worked fine.
but when I was trying to do the same in my office with other database it gave me follwing error.

SQL> SELECT ORA_ROWSCN,loc from dept where deptno=10;

ORA_ROWSCN LOC
---------- -------------
1.0385E+13 NEW YORK

SQL> SELECT SCN_TO_TIMESTAMP(ORA_ROWSCN),loc from dept where deptno=10;
SELECT SCN_TO_TIMESTAMP(ORA_ROWSCN),loc from dept where deptno=10
       *
ERROR at line 1:
ORA-08181: specified number is not a valid system change number
ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1


Please do tell what exactly is happening here. I tried to find out for ORA-08181 error but didnt 
find much information.


Followup   August 24, 2009 - 4pm Central time zone:

could well be that the SCN is just too old (we only keep a mapping for a short period of time)

ops$tkyte%ORA11GR1> select min(ora_rowscn) from sys.obj$;

MIN(ORA_ROWSCN)
---------------
           4126

ops$tkyte%ORA11GR1> select scn_to_timestamp(min(ora_rowscn)) from sys.obj$;
select scn_to_timestamp(min(ora_rowscn)) from sys.obj$
       *
ERROR at line 1:
ORA-08181: specified number is not a valid system change number
ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1


4 stars Privilege Required   August 25, 2009 - 12pm Central time zone
Reviewer: PS from UK
Tom, thanks for your explanation on ora_rowscn. But what is the privilege required to query 
ora_rowscn? User A & B exists on a DB. User A owns table T1 and has given select grant to User B. 
User B cannot execute the below statement, so just granting select does not cover privilege to 
select on ora_rowscn?

SELECT ORA_ROWSCN FROM A.T1;

Thanks, PS


Followup   August 25, 2009 - 8pm Central time zone:

ops$tkyte%ORA10GR2> create user a identified by a default tablespace users quota unlimited on 
users;

User created.

ops$tkyte%ORA10GR2> create user b identified by b;

User created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> grant create session, create table to a;

Grant succeeded.

ops$tkyte%ORA10GR2> grant create session to b;

Grant succeeded.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> connect a/a
Connected.
a%ORA10GR2> create table t ( x int );

Table created.

a%ORA10GR2> insert into t values (1);

1 row created.

a%ORA10GR2> commit;

Commit complete.

a%ORA10GR2> select x, ora_rowscn from t;

         X ORA_ROWSCN
---------- ----------
         1    4201062

a%ORA10GR2> grant select on t to b;

Grant succeeded.

a%ORA10GR2>
a%ORA10GR2> connect b/b
Connected.
b%ORA10GR2> select x, ora_rowscn from a.t;

         X ORA_ROWSCN
---------- ----------
         1    4201062




show us how to reproduce.
4 stars Follow up on privileges required   August 26, 2009 - 6am Central time zone
Reviewer: PS from UK
Thanks Tom. With the limited access I have this is what I could find out. 

User A owns the table T
Grants Select, Delete, Insert, Update on table T to Role R
User B is assigned the role R

Executing the query 

SELECT * FROM USER_ROLE_PRIVS; on user B gives the below result 

Username Granted_Role Admin_Option Default_Role OS_Granted
  B           R            NO          YES          NO

SELECT * FROM ROLE_SYS_PRIVS; on user B gives the below result

ROLE PRIVILEGE       ADMIN_OPTION
  R  CREATE SESSION       NO

On executing the select query it throws the error below

SELECT ORA_ROWSCN FROM A.T;

An error was encountered performing the requested operation:

ORA-00904: "ORA_ROWSCN": invalid identifier
00904. 00000 - "%s: invalid identifier"
*cause:
*Action:
Error at Line:1 Column:7

With this information, is it possible to find out the casue of the issue? I will try to find out 
the create user script for user B in parallel.

Thanks, PS.


Followup   August 26, 2009 - 7pm Central time zone:

take my test case

run it in a test system

alter it to match what YOU do in YOUR system and make it fail.

role or direct grant, it works for me and should work in general.



I want cut and pastes like I give you - I have no idea what you might really be typing.

ops$tkyte%ORA10GR2> drop user a cascade;

User dropped.

ops$tkyte%ORA10GR2> drop user b cascade;

User dropped.

ops$tkyte%ORA10GR2> drop role my_role;

Role dropped.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create user a identified by a default tablespace users quota unlimited on 
users;

User created.

ops$tkyte%ORA10GR2> create user b identified by b;

User created.

ops$tkyte%ORA10GR2> create role my_role;

Role created.

ops$tkyte%ORA10GR2> grant create session, create table to a;

Grant succeeded.

ops$tkyte%ORA10GR2> grant create session, my_role to b;

Grant succeeded.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> connect a/a
Connected.
a%ORA10GR2> create table t ( x int );

Table created.

a%ORA10GR2> insert into t values (1);

1 row created.

a%ORA10GR2> commit;

Commit complete.

a%ORA10GR2> select x, ora_rowscn from t;

         X ORA_ROWSCN
---------- ----------
         1    4234024

a%ORA10GR2> grant select on t to my_role;

Grant succeeded.

a%ORA10GR2>
a%ORA10GR2> connect b/b
Connected.
b%ORA10GR2> select x, ora_rowscn from a.t;

         X ORA_ROWSCN
---------- ----------
         1    4234024


Write a Review
 


All information and materials provided here are provided "as-is"; Oracle disclaims all express and implied warranties, including, the implied warranties of merchantability or fitness for a particular use. Oracle shall not be liable for any damages, including, direct, indirect, incidental, special or consequential damages for loss of profits, revenue, data or data use, incurred by you or any third party in connection with the use of this information or these materials.

About Oracle | Legal Notices and Terms of Use | Privacy Statement