Skip to Main Content
  • Questions
  • Clarification of "snapshot too old" error message

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Sujit.

Asked: March 14, 2017 - 8:36 am UTC

Last updated: March 14, 2017 - 11:00 am UTC

Version: 12c

Viewed 1000+ times

You Asked

Hi,
I was going through Tom's book and came across "snapshot too old" section. In the section small undo tablespace is created and below block is run.
Below block generates error message "snapshot too old". As per my understanding,the column values are in the cursor c after the first query. In the loop the fetching of the values are done. As the values are old and already stored in cursor, the loop will simply fetch the values stored in cursor. How the error is raised. Please explain.


ops$tkyte%ORA11GR2> declare
cursor c is
select /*+ first_rows */ object_name
from t
order by object_id;

l_object_name t.object_name%type;
l_rowcnt number := 0;
begin
open c;
loop
fetch c into l_object_name;
exit when c%notfound;
dbms_lock.sleep( 0.01 );
l_rowcnt := l_rowcnt+1;
end loop;
close c;
exception
when others then
dbms_output.put_line( 'rows fetched = ' || l_rowcnt );
raise;
end;
/
rows fetched = 301
declare
*
ERROR at line 1:
ORA-01555: snapshot too old: rollback segment number 46 with name
"_SYSSMU46_2555494716$" too small
ORA-06512: at line 21


Before the above block this block is run from other session

begin
for x in ( select rowid rid from t )
loop
update t set object_name = lower(object_name) where rowid = x.rid;
commit;
end loop;
end;
/

and Chris said...

A cursor is a pointer to a result set. Opening a cursor marks your intent to view data as of that point in time. It doesn't have the values the query returns. It's only when you fetch data from the cursor that you get the values.

This is why opening a cursor is instant: it doesn't get any data!

For example, if you create a function that sleeps for 1s and a 10 row table:

create or replace function f (v int)
  return int as 
begin
  dbms_lock.sleep(1);
  return v;
end;
/

drop table t purge;
create table t as
  select rownum x from dual connect by level <= 10;


A query that calls f for each row of t takes 10s:

SQL> set timing on
SQL> select f(x) from t;

      F(X)
----------
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10

10 rows selected.

Elapsed: 00:00:10.46


But opening the cursor for this query is instant:

SQL> var c refcursor;
SQL> begin
  2    open :c for
  3      select f(x) from t;
  4  end;
  5  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.31


It's only when you fetch the cursor that Oracle Database executes the query and gets the values:

SQL> print :c;

      F(X)
----------
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10

10 rows selected.

Elapsed: 00:00:10.31


So if at fetch time Oracle Database is unable to reconstruct how the data looked at the time you opened the cursor (because the undo was overwritten or whatever) you get ORA-01555.

Rating

  (1 rating)

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

Comments

Thanks a lot Chris for your prompt answer...

Sujit Pandey, March 14, 2017 - 12:02 pm UTC

Thanks a lot Chris for your prompt answer...

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