Skip to Main Content
  • Questions
  • Transactions and Isolation Level Confusion

Breadcrumb

May 4th

Question and Answer

Tom Kyte

Thanks for the question, Kevin.

Asked: September 27, 2003 - 4:46 pm UTC

Last updated: July 05, 2012 - 8:31 am UTC

Version: 8.1.7

Viewed 1000+ times

You Asked

Hi, Tom.

I have some questions about transactions and isolation levels that come from my reading of your books and answers in this forum, as well as the Oracle Database Concepts.

1. You've said that using serialazable isolation level does not negatively affect performance overall:

</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:776821414494,

However, the Concepts manual seems to indicate there is a performance tradeoff between serializable and read committed:

http://download-west.oracle.com/docs/cd/B10501_01/server.920/a96524/c21cnsis.htm#2799

Quoting from the section, "Read Committed Isolation":

"Read committed isolation can provide considerably more concurrency with a somewhat increased risk of inconsistent results due to phantoms and non-repeatable reads for some transactions."

"Many high-performance environments with high transaction arrival rates require more throughput and faster response times than can be achieved with serializable isolation."

Is this just a difference in scale?

2.  If there is not a significant performance difference (in the context of a particular application, of course) it seems to me that, in general, one would set the isolation mode to serializable instead of read committed so that one would have repeatable reads, etc.

Are there criteria you use to determine what you should set your isolation level to?  I'm mostly interested in OLTP applications.  I can't think of a good reason to set it to anything but serializable in my application, but I think I'd benefit from wider experience.

3.  Also, in the following link you seem to indicate that a session isn't always in a transaction in read committed mode (assuming that some SQL has been issued):

http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:8913646338893,

You make a similar statement in Expert One-on-One, chapter 4, "Transaction Control Statements":  "A transaction implicitly begins with the first statement that modifies data ...".

However, in the Concepts Guide, it seems to state clearly that one is always in a transaction if SQL has been executed:

http://download-west.oracle.com/docs/cd/B10501_01/server.920/a96524/c01_02intro.htm#54005 <code>

Can you resolve my confusion, here?

Thanks!

--Kevin


and Tom said...

1) i disagree with their conclusions.

We run our TPC-C's in serializable (it is a requirement of that benchmark). I would in fact say the opposite is many times true -- that serializable is one way to achieve high throughput and faster response times -- with less code.

2) no, no you wouldn't. serializable will increase the risk of transaction failure as the duration of the transaction increases. It is appropriate for exactly those high transaction arrival rates -- with small, fast, quick in out transactions. It is not in fact appropriate for many systems.

I use read committed almost exclusively. Serializable is only when I need multi-statement read consistency which is rare -- and even then, it is mostly with regards to reports and then I would use read only.

3) a select does not begin a transaction in read committed mode. a modification will. In serializable the "set" command marks the beginning of the transaction (IT would be the first statement)


Consider:


ops$tkyte@ORA920> select * from dual;

D
-
X

ops$tkyte@ORA920> set transaction isolation level serializable;

Transaction set.

If select marked the beginning, the set transaction would have FAILED like it does in the following:

ops$tkyte@ORA920> commit;

Commit complete.

ops$tkyte@ORA920> update emp set empno = 1 where rownum = 1;

1 row updated.

ops$tkyte@ORA920> set transaction isolation level serializable;
set transaction isolation level serializable
*
ERROR at line 1:
ORA-01453: SET TRANSACTION must be first statement of transaction


and the following shows the first statment of the transction is the SET command:

ops$tkyte@ORA920> set transaction isolation level serializable;

Transaction set.

ops$tkyte@ORA920> declare
2 pragma autonomous_transaction;
3 begin
4 delete from emp;
5 commit;
6 end;
7 /

PL/SQL procedure successfully completed.

Now, we'll query emp -- if we see data, then the transaction was already begun and the data is as of the point in time we did the set transaction

ops$tkyte@ORA920> select empno from emp;

EMPNO
----------
1
7499
7521
7566
7654
7698
7782
7788
7839
7844
7876
7900
7902
7934

14 rows selected.

ops$tkyte@ORA920> commit;

Commit complete.

ops$tkyte@ORA920> select empno from emp;

no rows selected

that shows that SET was the first statment of the transaction


The only statements that really begin transactions, that serve as start points, are modification statements and set transaction statements.

a select could if it was a select for update, but a select for update is really just a modification statement in disguise.

Rating

  (22 ratings)

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

Comments

TPC-C

malcolm, September 29, 2003 - 10:48 am UTC

> We run our TPC-C's in serializable (it is a requirement
> of that benchmark).

But I thought Oracle (and possibly mainframe DB2) is the only database to support serializable isolation level. I didn't think that any other databases had the mutli-version read consistancy which is required. So how do they publish TPC-C results without meeting the requirements? Or am I a victim of Oracle propaganda? ;-)

Tom Kyte
September 29, 2003 - 12:48 pm UTC

serializable does not require multi-versioning (and DB2 surely doesn't do multi-versioning).

serializable can be accomplished via read locks -- at the cost of concurrency.

Just to clarify

A reader, September 29, 2003 - 2:57 pm UTC

Multi-versioning can not help in the support of the serializable isolation level. So mayby you are a victim of Oracle propaganda :)

Tom Kyte
September 29, 2003 - 4:32 pm UTC


umm, our serializable model is entirely built on the premise of multi-versioning actually.


Oracles approach to serializable transactions is multi-versioning.
The others use locks.

no victims here...

RE: So mayby you are a victim of Oracle propaganda

Mark A. Williams, September 29, 2003 - 3:59 pm UTC

> So mayby you are a victim of Oracle propaganda

In what way? What elaborations can you make in regard to that statement?

- Mark

Victim of some kind...

malcolm, September 30, 2003 - 5:13 am UTC

Well, perhaps I'm a victim of some kind... I've been working solely with Oracle for so long that I've completely forgotten about the existence of 'read locks'. :-)

first transaction in Oracle

vkoul, February 15, 2004 - 3:10 am UTC

Sorry Tom, I was waiting for putting a question, but weeks after weeks I was not able to, so I am putting this question.

On Database startup what is the first transaction Oracle performs (I was asked to answer this question in an interview)

Tom Kyte
February 15, 2004 - 11:45 am UTC

trick question, one that is not answerable by the interviewers themselves unless they wrote the Oracle database itself (doubtful, no?)


I'd ask them, hmm, you mean transcations are happening when I do a startup nomount? Amazing - i did not know they could do that without any sort of redo logs, datafiles, whatever.

maybe they meant, "what does oracle sort of do upon startup", that would be a fair question -- one that most experienced DBA's should be able to answer in general. Things like "well, it use the init file to find the control files. it would use the control files to find the data/online redo log files. It would then see if crash recovery needed to be done. If so, it would use the redo to replay the transactions that were not yet recorded to the datafiles. It would then use the UNDO information to rollback transactions that were in flight when the db crashed and then ....blah blah blah...."

First Transaction In Oracle

VKOUL, February 17, 2004 - 1:22 pm UTC

Thanks Tom, You are the best ...

Switching isolation_level within a session.

Mathew Butler, March 27, 2006 - 7:39 am UTC

9.2.0.5 Win2K

I'm just looking at some reporting requirements which necessitate taking a consistent copy of data from various tables. Basically, we pull a consistent set of data out of one schema, load it with a "tag" into another schema ( for auditing purposes ) and then report on it.

My plan was to:

alter session set isolation_level = serializable
/
<collect consistent copy of data>
commit
/
alter session set isolation_level = read committed
/
<continue with remaining processing>


However, it doesn't look to me like this can be achieved without reconnecting to the DB after the commit. See below:

SQL> 
SQL> alter session set isolation_level = serializable
  2  /

Session altered

SQL> commit
  2  /

Commit complete

SQL> alter session set isolation_level = read committed
  2  /

alter session set isolation_level = read committed

ORA-01453: SET TRANSACTION must be first statement of transaction

Am I missing something, or is re-connecting the only way to avoid the ORA-01453?

Best Regards. 

Tom Kyte
March 27, 2006 - 10:06 am UTC

ops$tkyte@ORA10GR2> alter session set isolation_level = serializable;
 
Session altered.
 
ops$tkyte@ORA10GR2> commit;
 
Commit complete.
 
ops$tkyte@ORA10GR2> alter session set isolation_level = read committed;
 
Session altered.


I cannot reproduce your findings at all. In any release.

 

Peculiar

Mathew Butler, March 27, 2006 - 11:31 am UTC

Note to self: Always test in SQLPlus.

I was using the command window of PLSQL/Developer. I agree with your results using SQLPlus.

Mat.

Tom Kyte
March 27, 2006 - 3:14 pm UTC

I betcha plsql developer "optimized" away the commit on you ;)

Possible problem with serializable transactions....

Mathew Butler, July 03, 2006 - 7:03 am UTC

Any idea what is happening here?

I'm seeing some peculiar behaviour with serializable transactions. Test case is below - I think I am doing everything correctly. There are two tests, run as one script ( with a connect at the beginning of each test ). Both are logically equivalent ( one uses a single anonymous block to add some sata, the other uses two anonymous blocks.- no other differences )

I can see;

1) In test 1 more data being returned that was created from another transaction, after latreing to serializable transactions.
2) In test 2 an ORA-01877 for data that I believe I should be able to update.

I've highlighted the sections of the test below that are of interest.

Is this expected behaviourm, or am I missing something?


SQL*Plus: Release 9.2.0.5.0 - Production on Mon Jul 3 11:49:14 2006

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.

Connected to:
Oracle9i Enterprise Edition Release 9.2.0.5.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.5.0 - Production

SQL> connect test/test@mig1
Connected.
SQL>
SQL> /* setup */
SQL> drop table t;

Table dropped.

SQL> create table t (col1 number);

Table created.

SQL> create or replace procedure set_isolation_level_s is
  2  begin
  3    -- commit as isolation_level must be executed at start of transaction
  4    execute immediate 'alter session set isolation_level = serializable';
  5  end;
  6  /

Procedure created.

SQL> create or replace procedure set_isolation_level_rc is
  2  begin
  3    execute immediate 'alter session set isolation_level = read committed';
  4  end;
  5  /

Procedure created.

SQL> create or replace procedure add_data_to_t ( p_val t.col1%type ) is
  2    pragma autonomous_transaction;
  3  begin
  4    insert into t values (p_val);
  5    commit;
  6  end;
  7  /

Procedure created.

SQL> /* end setup */

SQL> /* Test 1 */

SQL> begin
  2    add_data_to_t(1);
  3  end;
  4  /

PL/SQL procedure successfully completed.

SQL> insert into t values(11);

1 row created.

SQL> commit;

Commit complete.

SQL> begin
  2    set_isolation_level_s;
  3  end;
  4  /

PL/SQL procedure successfully completed.

SQL> -- add some more data
SQL> begin
  2    add_data_to_t(2);
  3  end;
  4  /

PL/SQL procedure successfully completed.

SQL> begin
  2    add_data_to_t(3);
  3  end;
  4  /

PL/SQL procedure successfully completed.

SQL> -- check data - should only be able to see 1, 11
SQL> select * from t;

<b>      COL1
----------
         1
        11
         2
         3</b>

SQL> -- update data created by other session prior to serializable change
SQL> update t set col1 = 5 where col1 = 1;

1 row updated.

SQL> /* Test 2*/

SQL> connect test/test@mig1
Connected.

SQL> delete from t;

4 rows deleted.

SQL> commit;

Commit complete.

SQL>
SQL> -- add some data in seperate transaction
SQL> begin
  2    add_data_to_t(1);
  3  end;
  4  /

PL/SQL procedure successfully completed.

SQL> insert into t values(11);

1 row created.

SQL> -- set the isolation level so will report on this point in time
SQL> commit;

Commit complete.

SQL> begin
  2    set_isolation_level_s;
  3  end;
  4  /

PL/SQL procedure successfully completed.

SQL> -- check data
SQL> select * from t;

      COL1
----------
         1
        11

SQL> -- add some more data
SQL> begin
  2    add_data_to_t(2);
  3    add_data_to_t(3);
  4  end;
  5  /

PL/SQL procedure successfully completed.

SQL> -- check data
SQL> select * from t;

<b>      COL1
----------
         1
        11</b>

SQL> -- update data created by other session
SQL> update t set col1 = 5 where col1 = 1;
update t set col1 = 5 where col1 = 1
       *
ERROR at line 1:
<b>ORA-08177: can't serialize access for this transaction</b>

 

Tom Kyte
July 07, 2006 - 3:29 pm UTC

you will get "false positives" - the ora-8177s - at the block level in some cases due to the implementation of serializable in Oracle.

</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:7636765105002 <code>

Mathew Butler, July 10, 2006 - 7:38 am UTC

Many thanks for the response.

* From the response I understand that the SCN used to define the start of the transaction is stored in the block header. As such I should find that the ORA-01877 was raised in my test when data from both sessions was created on the same block - I've shown this in the test below. <b>Is my understanding correct?</b>

* What about the unexpected additional rows that are returned in test 1?? ( I've bolded then in the re-test below). <b>Is this the same effect?</b>

* <b>Is there a reliable means of achieving a consistent read throughout a transaction - does flashback query with tables re-built with rowdependencies give me this?

I'm trying to satisfy some peculiar reporting requirements and need to obtain a consistent view of the data across the transaction with minimal extra code ie: handling of unexpected ORA-01877 and possibly also allow some updates.</b>

Here is the test including block information:


SQL> connect test/test@mig1
Connected.

SQL> /* setup */
SQL> drop table t;

Table dropped.

SQL>
SQL> create table t (col1 number);

Table created.

SQL>
SQL> create or replace procedure set_isolation_level_s is
  2  begin
  3      -- commit as isolation_level must be executed at start of transaction
  4      execute immediate 'alter session set isolation_level = serializable';
  5  end;
  6  /

Procedure created.

SQL> create or replace procedure set_isolation_level_rc is
  2    begin
  3      execute immediate 'alter session set isolation_level = read committed';
  4    end;
  5  /

Procedure created.

SQL> create or replace procedure add_data_to_t ( p_val t.col1%type ) is
  2      pragma autonomous_transaction;
  3    begin
  4      insert into t values (p_val);
  5      commit;
  6    end;
  7  /

Procedure created.

SQL> /* end setup */

SQL> /* Test 1 */
SQL> begin
  2      add_data_to_t(1);
  3  end;
  4  /

PL/SQL procedure successfully completed.

SQL> insert into t values(11);

1 row created.

SQL> commit;

Commit complete.

SQL> begin
  2      set_isolation_level_s;
  3    end;
  4  /

PL/SQL procedure successfully completed.

SQL> -- add some more data
SQL> begin
  2      add_data_to_t(2);
  3    end;
  4  /

PL/SQL procedure successfully completed.

SQL> begin
  2      add_data_to_t(3);
  3    end;
  4  /

PL/SQL procedure successfully completed.

SQL> -- check data - should only be able to see 1, 11
SQL> select col1,dbms_rowid.rowid_block_number(rowid) from t;

      COL1 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
         1                                   32
        11                                   32
<b>         2                                   32
         3                                   32</b>

SQL> -- update data created by other session prior to serializable change
SQL> update t set col1 = 5 where col1 = 1;

1 row updated.

SQL>
SQL> /* Test 2*/
SQL> connect test/test@mig1
Connected.

SQL> delete from t;

4 rows deleted.

SQL> commit;

Commit complete.

SQL> -- add some data in seperate transaction
SQL> begin
  2      add_data_to_t(1);
  3    end;
  4  /

PL/SQL procedure successfully completed.

SQL> insert into t values(11);

1 row created.

SQL> -- set the isolation level so will report on this point in time
SQL> commit;

Commit complete.

SQL> begin
  2      set_isolation_level_s;
  3    end;
  4  /

PL/SQL procedure successfully completed.

SQL> -- check data
SQL> select col1,dbms_rowid.rowid_block_number(rowid) from t;

      COL1 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
         1                                   32
        11                                   32

SQL> -- add some more data
SQL> begin
  2      add_data_to_t(2);
  3      add_data_to_t(3);
  4  end;
  5  /

PL/SQL procedure successfully completed.

SQL> -- check data
SQL> select col1,dbms_rowid.rowid_block_number(rowid) from t;

      COL1 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
         1                                   32
        11                                   32

SQL> -- update data created by other session
SQL> update t set col1 = 5 where col1 = 1;
update t set col1 = 5 where col1 = 1
       *
ERROR at line 1:
ORA-08177: can't serialize access for this transaction

 

Tom Kyte
July 10, 2006 - 8:42 am UTC

8177's can be thrown when they technically need not be, it is an implementation restriction.

flashback query will give you consistent read throughout the transaction - across transactions in fact.

the "extra rows" are not "extra rows", you set your sessions default isolation level to "serializable", that kicks in at the first statement of a new transaction, you did:

o set default isolation to serializable
o run transaction and commit (you have not yet started a transaction for yourself)
o run transaction and commit (ditto)
o started your "transaction" by running the first statement, the select, it
  is serializable "as of now"

eg:

ops$tkyte@ORA10GR2> exec set_isolation_level_s;
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA10GR2> exec add_data_to_t(2);
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA10GR2> exec add_data_to_t(3);
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA10GR2> select col1,dbms_rowid.rowid_block_number(rowid) from t;
<b>this is the beginning of YOUR 'transaction'</b>
 
      COL1 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
         1                                   80
        11                                   80
         2                                   80
         3                                   80
 
ops$tkyte@ORA10GR2> exec add_data_to_t(4);
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA10GR2> select col1,dbms_rowid.rowid_block_number(rowid) from t;
 
      COL1 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
         1                                   80
        11                                   80
         2                                   80
         3                                   80

<b>See, you still only see 4 rows, but commit:</b>
 
ops$tkyte@ORA10GR2> commit;
 
Commit complete.
 
ops$tkyte@ORA10GR2> select col1,dbms_rowid.rowid_block_number(rowid) from t;
 
      COL1 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
         1                                   80
        11                                   80
         2                                   80
         3                                   80
         4                                   80

<b>and you see them all, now if you change:</b>

ops$tkyte@ORA10GR2> create or replace procedure set_isolation_level_s is
  2  begin
  3      -- commit as isolation_level must be executed at start of transaction
  4      execute immediate 'alter session set isolation_level = serializable';
  5      execute immediate 'set transaction isolation level serializable';
  6  end;
  7  /


<b>to both change the DEFAULT ISOLATION LEVEL as well as BEGIN A TRANSACTION... then:</b>

ops$tkyte@ORA10GR2> exec set_isolation_level_s;
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA10GR2> exec add_data_to_t(2);
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA10GR2> exec add_data_to_t(3);
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA10GR2> select col1,dbms_rowid.rowid_block_number(rowid) from t;
 
      COL1 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
         1                                   88
        11                                   88
 
ops$tkyte@ORA10GR2> exec add_data_to_t(4);
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA10GR2> select col1,dbms_rowid.rowid_block_number(rowid) from t;
 
      COL1 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
         1                                   88
        11                                   88
 
ops$tkyte@ORA10GR2> commit;
 
Commit complete.
 
ops$tkyte@ORA10GR2> select col1,dbms_rowid.rowid_block_number(rowid) from t;
 
      COL1 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
         1                                   88
        11                                   88
         2                                   88
         3                                   88
         4                                   88
 
<b>you do not 'see the extra rows', since your plsql call to set the isolation level started a transaction itself</b>


 

I see!

Mathew Butler, July 10, 2006 - 9:18 am UTC

Very happy with this explanation. Thanks for the nod about the "alter session" and "set transaction" I had previously understood these to do the same thing.

This next question may be important to the implementation of this report:

Under what circumstances can 8177's can be thrown when they technically need not be?

Briefly, the report may also need to insert/update a few rows of data. It is extremely likely that these rows may be on the same block as rows previously inserted by other processes. My current understanding is that an 8177 may be raised in this case. So some concern here...

...obviously if one is raised I can catch it, but would probably need an autonomous transaction "fudge" in order to make the changes. This messes with my transaction integrity on failure.

Do I have good reason for my concern?



Tom Kyte
July 10, 2006 - 9:24 am UTC

there is no real documentation on when they will be thrown when not needed, just understand they will be and the application needs to be aware of that.

serializable is designed for extremely short duration OLTP situations. It is not something to be considered for long running reports (read only OR flashback query is)

Further info request...

Mathew Butler, July 10, 2006 - 9:45 am UTC

>>there is no real documentation on when they will be
>>thrown when not needed, just understand they will be and
>>the application needs to be aware of that.

OK, "maybe fine" - but again, what does one do if one is encountered when not needed?

>>serializable is designed for extremely short duration
>>OLTP situations. It is not something to be considered
>>for long running reports (read only OR flashback query
>>is)

Can you explain why not intended for long transactions? - I think my requirement may be a "special case".

The reporting process is to do the following:
- run as a single process ( no other concurrent reporting processes..currently )
- pull a consistent snapshot of data from schema1 and load into schema2
- store some data in schema1 about the fact that this process has occurred ( one insert and one update )
- once data successfully transferred then commit
- proceed to generate several reports on copy of data in schema2 in "read committed" mode.

The whole process is intended to run in < 1 hour.
There will be minimal concurrent updates of schema1 data during the copy - maybe 1 or two versions of each copied block

I'm thinking about this in detail as I really think that flashback query is the "best" answer however, I've been asked to come up with a solution that minimises code change ( this is an enhancement to an existing report that currently uses user defined application locks to serialise access during the copy - looking for a solution to allow concurrent access )


Tom Kyte
July 10, 2006 - 9:53 am UTC

one retries the transaction or one fails the transaction.
eg: what does one do when one recieves ora-60, deadlock detected?



They (serializable transactions) are not designed, intended, useful, workable for long running transactions. "by definition", that is just a fact.




I can't drop this one, just yet...

Mathew Butler, July 10, 2006 - 10:04 am UTC

>one retries the transaction or one fails the transaction.
>eg: what does one do when one recieves ora-60, deadlock
>detected?

Accepted - and also a good reason not to use serializable for "long" transactions as "long" tx => "long" time to rollback and re-try. Will have to test to see how much time this rollback/re-try might take and identify whether this is acceptable. If not acceptable then flashback query will look more promising.

Thanks again.

Isolation Level Read Only

Mark, July 10, 2006 - 10:43 am UTC

Hi Tom,

Regarding a high-load, Read-Only reporting database, is it advantageous (performance-wise) to set the isolation level = read only as opposed to the default value?

8i Standard 8.1.7.4 (freakin' dinosaur)

Regards,
Mark

Tom Kyte
July 10, 2006 - 12:37 pm UTC

nope, it would either have no effect or cause extra work (because unless the database is opened read only, there is not any such thing as a read only database)

likely the answer is "it would have no effect".

read only would simply freeze the SCN at the beginning of the transaction - the same amount of work would be done to put a block back the way it was as of that SCN as it would to put it back the way it was if the SCN was set when the statement is opened in your case: none, the block isn't being modified.

Isolation Level

V, December 04, 2006 - 2:30 pm UTC

Tom, I have the following which I have inherited:

create or replace PROCEDURE ins_suggestion
(
isubj IN VARCHAR2,
iattach_flg IN CHAR,
iqual_flag IN CHAR,
isfty_flag IN CHAR,
icost_flag IN CHAR,
ifunction IN VARCHAR2,
ilob_name IN VARCHAR2,
ibenefit IN VARCHAR,
icompetitive_flag IN CHAR,
isite_name IN VARCHAR,
icur_methprob IN VARCHAR,
isubmit IN VARCHAR,
iprop_methprob IN VARCHAR
)

IS

cur_esn_num VARCHAR2(11);
isite_id VARCHAR2(3);
icoor_emp_num VARCHAR2(10);
ianly_emp_num VARCHAR2(10);
isug_stat VARCHAR2(2);
iemp_name VARCHAR2(50);
iemp_dept VARCHAR2(10);
iemp_phone VARCHAR2(14);
l_curr_yr char(4);
p_curr_yr char(4);
l_dummy number;

BEGIN
--
--
select curr_yr into l_curr_yr from esn_seq_num;
select to_char(sysdate,'YYYY') INTO p_curr_yr FROM DUAL;
IF l_curr_yr <> p_curr_yr THEN
UPDATE esn_seq_num set curr_yr = to_char(sysdate,'YYYY');
COMMIT;
EXECUTE IMMEDIATE 'SELECT esn.nextval from dual' INTO l_dummy;
EXECUTE IMMEDIATE 'alter sequence esn increment by -'||(l_dummy)||' minvalue 0';
EXECUTE IMMEDIATE 'SELECT esn.nextval from dual' INTO l_dummy;
EXECUTE IMMEDIATE 'alter sequence esn increment by 1';
END IF;
SELECT esn.nextval INTO cur_esn_num FROM DUAL;
select
case when length(cur_esn_num)=4 then to_char(sysdate,'MMYY')||cur_esn_num
when length(cur_esn_num)=3 then to_char(sysdate,'MMYY')||'0'||cur_esn_num -- 100 0100
when length(cur_esn_num)=2 then to_char(sysdate,'MMYY')||'00'||cur_esn_num -- 10 0010
when length(cur_esn_num)=1 then to_char(sysdate,'MMYY')||'000'||cur_esn_num -- 1 0001
--else 0
end
INTO cur_esn_num from dual;
-- Removed in support if new ESN FORMAT MMYY####
--SELECT last_esn + 1 INTO cur_esn_num FROM esn_seq_num;
UPDATE esn_seq_num SET last_esn = cur_esn_num;
...
...
INSERT into table
commit;

EXCEPTION
WHEN dup_val_on_index THEN
dbms_output.put_line('Duplicate ' ||
SQLERRM);
RAISE;
WHEN OTHERS THEN
dbms_output.put_line('Error: ' ||
SQLERRM);
RAISE;

END ins_suggestion;

As you notice, I am resetting the sequence in the begining of the year. I believe I need to set some isolation level here in case the insert fails but am not sure.

Tom Kyte
December 05, 2006 - 9:35 pm UTC

sorry, no clue what you are really doing here. All I see is a bunch of code which may or may not accomplish whatever goal you may or may not have in mind.

What I don't see is "a goal"

no clue why you would want to reset something at the beginning of the year - or how you can tell "we are the first of the year" or whatever.

Goal

V, December 06, 2006 - 9:46 am UTC

The goal here is to have a unique # to identify a row with the format of: MMYY#### i.e. 12060001 When the Year changes the # should be 01070001 . This is a app that was inherited and adding or removing attributes is not possible because of tight time constraints.

I was attempting to assign the # in the already existing SP. Prior to the change, the # was assigned like:
SELECT last_esn + 1 INTO cur_esn_num FROM esn_seq_num;
UPDATE esn_seq_num SET last_esn = cur_esn_num;


Tom Kyte
December 07, 2006 - 8:36 am UTC

why


how do you KNOW you are the first record of the YEAR???????

every time you insert, you'd have to ask yourself that question "am I the first??"

and in order to answer that - you have to obviously SERIALIZE ALL INSERTS.

even in AUGUST - because it might be AUGUST when you first insert.


Now, ask yourself - do you really really want to do that??????




Isolcation NOT for Object's Existance ?

Yogesh Purabiya, April 14, 2007 - 3:16 am UTC

(1) What is the difference between
set transaction isolation level serializable;
AND
alter session set isolation_level = serializable;

(2) Following is the spool output of a session.
Statement "select * from x;" gave error first time, since there was no such table.
Once created (in a different session), it gave no errors.
So, is the existance of an object not marked with SCN ?

12:07:01 stores1@ >c/set /
1* alter session set isolation_level = serializable
12:07:06 stores1@ >/

Session altered.

real: 15
12:07:08 stores1@ >select * from x;
select * from x
*
ERROR at line 1:
ORA-00942: table or view does not exist


real: 0
12:07:12 stores1@ >/

no rows selected

real: 15
12:07:23 stores1@ >
12:07:47 stores1@ >spo off

Tom Kyte
April 16, 2007 - 10:35 am UTC

1) transaction vs session

single transaction till commit, versus rest of session until exit.


ops$tkyte%ORA10GR2> create table t ( x int );

Table created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> insert into t values ( 1 );

1 row created.

ops$tkyte%ORA10GR2> commit;

Commit complete.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> set transaction isolation level serializable;

Transaction set.

ops$tkyte%ORA10GR2> select * from t;

         X
----------
         1

ops$tkyte%ORA10GR2> declare
  2          pragma autonomous_transaction;
  3  begin
  4          update t set x = x+1;
  5          commit;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> select * from t;

         X
----------
         1

ops$tkyte%ORA10GR2> commit;

Commit complete.

ops$tkyte%ORA10GR2> select * from t;

         X
----------
         2

ops$tkyte%ORA10GR2> declare
  2          pragma autonomous_transaction;
  3  begin
  4          update t set x = x+1;
  5          commit;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> select * from t;

         X
----------
         3

<b>we are not serializable anymore - we see the update.....</b>

ops$tkyte%ORA10GR2> alter session set isolation_level = serializable;

Session altered.

ops$tkyte%ORA10GR2> select * from t;

         X
----------
         3

ops$tkyte%ORA10GR2> declare
  2          pragma autonomous_transaction;
  3  begin
  4          update t set x = x+1;
  5          commit;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> select * from t;

         X
----------
         3

ops$tkyte%ORA10GR2> commit;

Commit complete.

ops$tkyte%ORA10GR2> select * from t;

         X
----------
         4

ops$tkyte%ORA10GR2> declare
  2          pragma autonomous_transaction;
  3  begin
  4          update t set x = x+1;
  5          commit;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> select * from t;

         X
----------
         4

<b>we are serializable even after the commit - session....</b>





2) I see NO CONFLICT with serializable here.

Isolation with DDL

Yogesh Purabiya, April 17, 2007 - 11:35 pm UTC

Thanks a lot, as usual.
It is really a nice and clear reply.

ORA-08177 errors

Doug Floyd, November 28, 2007 - 9:53 am UTC

Is there any possible way that ORA-08177 could occur unless the isolation level is set to something other than the default read committed?

The reason I ask is one of our databases has been receiving this error recently when running an update statement and I'm trying to prove to our developers that it is happening because the application they are using (SAS) must be changing the isolation level. Is there a way I can determine what isolation level they are using when the error occurs (through a v$ view or otherwise)?

Thanks!
Tom Kyte
November 28, 2007 - 11:07 pm UTC

$ oerr ora 8177
08177, 00000, "can't serialize access for this transaction"
// *Cause:   Encountered data changed by an operation that occurred after
//           the start of this serializable transaction.
// *Action:  In read/write transactions, retry the intended operation or
//           transaction.



transaction is serializable - I'm not aware of any other circumstance you would get that.

trace a SAS session and see if they issue this.

what would happened

A reader, January 16, 2009 - 1:00 am UTC

Tom,

I have two session running in this fashion, assume table t have 1000 records.

Time  Session 1                           Session 2
0     declare x number;
1     begin
2        select count(*) into x           
         from t;                          begin
3        --do another work                  delete t;
4        --do another work                  commit;
5        --do another work                end;
6       select count(*) into x
        from t;
7     end;


For the first select on t at session 1, Oracle returning 1000 rows as I expected, but for the second select what the number will be returned ? Zero or 1000, Tom ?

Thanks.
Tom Kyte
January 16, 2009 - 6:16 pm UTC

depends on the isolation level.

Using standard read committed - the counts at time t2 and t6 would be different since the delete and commit that happened at times t3 and t4 would be visible to t6.

Using serializable or read only transactions, t2 and t6 would report the same count.

The Concepts Manual 11.2 on serializable

Milan, April 27, 2011 - 8:23 am UTC

Hi Tom.
The Concepts manual still insists that there is a performance tradeoff between serializable and read committed:
http://download.oracle.com/docs/cd/E11882_01/server.112/e16508/consist.htm#i13945
In particular:
<quote>
Complete isolation of concurrently running transactions could mean that one transaction cannot perform an insertion into a table being queried by another transaction.
</quote>
I just can't think of such scenario.
Readers should NEVER block writers, right?
If the above excerpt from the concepts manual is wrong, can you please use your authority to make them rectify the misleading statements.
Thank you for all the things that I learned from you.
Tom Kyte
April 27, 2011 - 9:13 am UTC

Sorry, I can see how that would be confusing. What we were trying to say is more along the lines of:


To describe consistent transaction behavior when transactions run concurrently, database researchers have defined a transaction isolation model called serializability. A serializable transaction operates in an environment that makes it appear as if no other users were modifying data in the database.

While this degree of isolation between transactions is generally desirable, running many applications in serial would seriously compromise application throughput. Complete isolation of concurrently running transactions would mean that one transaction cannot perform an insert into a table being queried by another transaction. In short, real-world considerations usually require a compromise between perfect transaction isolation and performance.

Oracle Database maintains data consistency by using a multiversion consistency model and various types of locks and transactions. In this way, the database can present a view of data to multiple concurrent users, with each view consistent to a point in time. Because different versions of data blocks can exist simultaneously, transactions can read the version of data committed at the point in time required by a query and return results that are consistent to a single point in time.



We were trying to convey that whilst serializable gives the appearance of "single user", it does so (in Oracle) in a highly concurrent fashion.

ioslation level and multi-version

A reader, July 04, 2012 - 11:21 pm UTC

Hi Tom,

In your book 'expert-oracle-database-architecture-2ND' page 247, you talking about the multi-version and isolation level.

Table 7-3. ACCOUNTS Table During Modifications
Row Account Number Account Balance Locked?
1 123 ($500.00) changed to $100.00 X
2 456 $240.25 --
... ... ... --
342,023 987 ($100.00) changed to $500.00 X

select sum(account_balance) from accounts;

I have a question that given the default behaviour is read committed in oracle.

Suppose the sum has go over row 1 which means it already take $500,
and then just before it hit the last row(342,023), the other transaction committed.
So 342,023 is 500 rather than 100 now, but row 1 already summed, will it be a issue that
400 was calculated twice?
Tom Kyte
July 05, 2012 - 8:31 am UTC

with multi-versioned READ CONSISTENCY - all of the blocks used by a statement are all as of the same point in time.

If the other session had committed, we would have noticed the timestamp of the last block read was newer than our query start time and we would have rolled back the modification to the point in time our query began.

Every block used by the query will be as of the same point in time. It will be read consistent. If a block is locked - we can read around the lock, if the block was modified - we roll back the changes so as to see what was committed in the database as of the time our query (or transaction in serializable) began

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library