Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Sokrates.

Asked: March 29, 2012 - 9:18 am UTC

Last updated: August 17, 2012 - 12:32 pm UTC

Version: 11.2.0

Viewed 1000+ times

You Asked

When are implicit cursors bulk-processed ?
When are they not bulk-processed ?
When they are bulk-processed, what is the bulk-size and what does it depend on ?

exec dbms_monitor.session_trace_enable
begin
   for x in (select * from big_table where rownum <= 1e7) loop
      if x.somenumbercolumn < 0 then null; end if;
   end loop;
end;
/


tkprof shows me
SELECT *
FROM
 BIG_TABLE WHERE ROWNUM <= 1E7


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch   100001    103.39     104.21      82013     180411          0    10000000
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total   100003    103.39     104.22      82013     180411          0    10000000


so, bulk-size approximately
100 = 10000000 / 100001 ( why 100001 fetches instead of 100000 ?)


create or replace type numbertable 
as 
table of number
/

create or replace function f(vuntil in int, vsleep in number) return numbertable pipelined is
  i number := 1;
begin
  loop
    exit when i>vuntil;
    pipe row(i); i:=i + 1;
    dbms_lock.sleep(vsleep);
  end loop;
  
  return;
end f;
/


exec dbms_monitor.session_trace_enable
set timi on

declare 
  start_ number;
begin
   start_ := dbms_utility.get_time;
   for x in (
      select *
      from table(f(vuntil => 120, vsleep => 1 / 100))
   ) loop
      dbms_output.put_line(
         x.column_value || ': ' ||
         (dbms_utility.get_time - start_)
      );
   end loop;
end;
/


gives the output
1: 111
2: 111
3: 111
4: 111
5: 111
6: 111
...
95: 111
96: 111
97: 111
98: 111
99: 111
100: 111
101: 134
102: 134
103: 134
...
117: 134
118: 134
119: 134
120: 134


So we also fetched 100 rows in the first fetch (sleeping a centisecond for each row, which gives around 1 second in total before the first row is processed), and 20 rows in the second fetch.

tkprof confirms that:

SELECT *
FROM
 TABLE(F(VUNTIL => 120, VSLEEP => 1 / 100))


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0         30          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       1.33          0          0          0         120
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       1.33          0         30          0         120


So, is bulk size in implicit cursors always 100 ?

and Tom said...

Implicit cursor for loops array fetch 100 rows at a time by default in 10g and above (in 9i they did not, it was single row).

It can be reduced to 1 by turning down the plsql optimization level during your compilation.


It was 100001 fetches because we needed a last fetch to find out "hey, there is no more data". Reduce your table by 1 row and it would be 100000 fetches since the last fetch of 100 rows would only find 99 and get the "no data found"


The number 100 is fixed and not alterable (except to downgrade it to 1 that is).



Implicit cursors in the form of "select bulk collect" use an array size of "infinity" - they do a single fetch call to retrieve all data. (so be careful, sometimes if you get what you ask for, you get something you didn't really want!)

Rating

  (4 ratings)

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

Comments

Bulk Fetch

A reader, March 29, 2012 - 11:20 am UTC

If I have code like the following:

Declare
   cursor cur is
      SELECT OBJECT_NAME
      FROM ALL_OBJECTS;
BEGIN
   FOR C IN CUR LOOP
      NULL;
   END LOOP;
END;

Will the above code also do an automatic bulk fetch? or Do I have to write 
FOR C IN ( SELECT OBJECT_NAME
      FROM ALL_OBJECTS) LOOP....
to do an automatic  bulk fetch?





Tom Kyte
March 29, 2012 - 11:57 am UTC

you should have been able to answer that! Sokrates showed you how above (and the method is something I've been showing you how to do for going on 20 years now...)


Answer: yes, it will. That is an implicit cursor for loop. There is no real difference between

for x in cursor loop

for x in (select ....) loop

they are both implicit cursors, they are equivalent in all respects.

magic number 100

Sokrates, March 30, 2012 - 2:36 am UTC

...The number 100 is fixed and not alterable...

and not documented either, am I correct ?
( couldn't find it in PL/SQL Language Reference )




Tom Kyte
March 30, 2012 - 7:13 am UTC

The various optimizations implemented by the plsql_optimize_level

http://docs.oracle.com/cd/E11882_01/server.112/e25513/initparams194.htm#REFRN10255

are not explicitly documented in detail, no.

for update

Sokrates, August 07, 2012 - 8:37 am UTC

when I change my first example into
exec dbms_monitor.session_trace_enable
begin
   for x in (select * from big_table where rownum <= 1e7 for update) loop
      if x.somenumbercolumn < 0 then null; end if;
   end loop;
end;
/


I get
SELECT *
FROM
 BIG_TABLE WHERE ROWNUM <= 1E7 FOR UPDATE


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1    154.72     519.86     601662      81884     412364           0
Fetch   10000001    103.33     197.40     328923          0          0    10000000
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total   10000003    258.06     717.27     930585      81884     412364    10000000
,

so no plsql optimization takes place.

Why?
Tom Kyte
August 17, 2012 - 12:32 pm UTC

It used to - but that was declared a bug because it changed the semantics somewhat.

ops$tkyte%ORA10GR2> create table t as select * from all_objects where rownum <= 202;

Table created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> begin
  2     for x in (select * from t where rownum <= 1e7 for update) loop
  3        dbms_output.put_line( 'object id = ' || x.object_id );
  4        commit;
  5     end loop;
  6  end;
  7  /
object id = 20
begin
*
ERROR at line 1:
ORA-01002: fetch out of sequence
ORA-06512: at line 2



if we array fetched - you would see us processing the first 100 rows and then failing. It changes the semantics of select for update if you implicitly array fetch.

You have to do that explicitly.

ok thanks

Sokrates, August 17, 2012 - 2:33 pm UTC


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