Skip to Main Content
  • Questions
  • Invalid cursor when using a bulk collect

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Tony.

Asked: May 22, 2005 - 11:07 pm UTC

Last updated: July 01, 2006 - 7:50 am UTC

Version: 8.1.7.4

Viewed 1000+ times

You Asked

Tom,

I am calculating some percentile values using cume_dist analytic function as well as lag and lead functions. I need to store these values in an array to allow a table to be updated later.

The example uses "user_tables" view for simplicity.

I tried this code but got:

SQL> l
1 declare
2 type percentile_cur_typ is ref cursor;
3
4 c_percentile percentile_cur_typ;
5
6 type student_rec_typ is record(
7 time_series dbms_sql.date_table,
8 year_level dbms_sql.varchar2_table,
9 benchmark_student_group dbms_sql.varchar2_table,
10 strand dbms_sql.varchar2_table,
11 score dbms_sql.number_table,
12 percentile25 dbms_sql.number_table,
13 percentile75 dbms_sql.number_table
14 );
15
16 v_rec student_rec_typ;
17 v_bigstr varchar2(10000);
18 begin
19 v_bigstr :=
20 'select
21 last_analyzed,
22 lag(table_name) over (order by table_name) xx,
23 tablespace_name,
24 cluster_name,
25 num_rows,
26 blocks,
27 empty_blocks
28 from
29 user_tables';
30
31 open c_percentile for select
32 last_analyzed,
33 lag(table_name) over (order by table_name) xx,
34 tablespace_name,
35 cluster_name,
36 num_rows,
37 blocks,
38 empty_blocks
39 from
40 user_tables;
41
42 fetch c_percentile
43 bulk collect into v_rec.time_series,
44 v_rec.year_level,
45 v_rec.benchmark_student_group,
46 v_rec.strand,
47 v_rec.score,
48 v_rec.percentile25,
49 v_rec.percentile75;
50
51 close c_percentile;
52
53 dbms_output.enable(1000000);
54 dbms_output.put_line(v_rec.year_level.count);
55 end;
56* ;
SQL> /
lag(table_name) over (order by table_name) xx,
*
ERROR at line 33:
ORA-06550: line 33, column 34:
PLS-00103: Encountered the symbol "(" when expecting one of the following: , from into bulk

The select statement is perfectly fine on its own. To solve the problem I put the select statement into a variable. This method has worked before, but now its complaining about the fetch statement because I'm using a "bulk collect".


SQL> l
1 declare
2 type percentile_cur_typ is ref cursor;
3
4 c_percentile percentile_cur_typ;
5
6 type student_rec_typ is record(
7 time_series dbms_sql.date_table,
8 year_level dbms_sql.varchar2_table,
9 benchmark_student_group dbms_sql.varchar2_table,
10 strand dbms_sql.varchar2_table,
11 score dbms_sql.number_table,
12 percentile25 dbms_sql.number_table,
13 percentile75 dbms_sql.number_table
14 );
15
16 v_rec student_rec_typ;
17 v_bigstr varchar2(10000);
18 begin
19 v_bigstr :=
20 'select
21 last_analyzed,
22 lag(table_name) over (order by table_name) xx,
23 tablespace_name,
24 cluster_name,
25 num_rows,
26 blocks,
27 empty_blocks
28 from
29 user_tables';
30
31 open c_percentile for v_bigstr;
32
33 fetch c_percentile
34 bulk collect into v_rec.time_series,
35 v_rec.year_level,
36 v_rec.benchmark_student_group,
37 v_rec.strand,
38 v_rec.score,
39 v_rec.percentile25,
40 v_rec.percentile75;
41
42 close c_percentile;
43
44 dbms_output.enable(1000000);
45 dbms_output.put_line(v_rec.year_level.count);
46* end;
SQL>
SQL>
SQL> /
declare
*
ERROR at line 1:
ORA-01001: invalid cursor
ORA-06512: at line 33

If I remove the "bulk collect" it works, but I need to put these values into a table of arrays. I have been trying to use some native dynamic SQL to resolve this but I've had no luck. I have read the documentation about native dynamic SQL but it hasn't helped.

Is there a way to achieve this using Oracle 8.1.7.4 and if so how would you do it?

Regards
Tony





and Tom said...

It is an 8i issue.

In 8i, lag, lead, (analytics) and other SQL functions are not "recognized" by the plsql SQL parser.

You can sometimes work around that by using dynamic SQL -- but native dynamic sql does not allow for bulk collect in 8i either (added later in 9i).

So you can either:

a) use single row fetches and dynamic sql
b) a view (hide the analytics from plsql) and static sql with bulk collect
c) dbms_sql with array fetching

Rating

  (2 ratings)

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

Comments

Tony Cristiano, May 23, 2005 - 11:01 pm UTC

Tom,

That's what I needed to know. I have re-written the code using your option a) "use single row fetches and dynamic sql"

Here is the new version. Can you see any improvements I can make especially regarding the record of arrays.

You also mentioned there are other SQL functions not recognized by the plsql SQL parser. Can you give some examples of these?

declare
type percentile_cur_typ is ref cursor;

c_percentile percentile_cur_typ;

type student_rec_typ is record(
time_series dbms_sql.date_table,
year_level dbms_sql.varchar2_table,
benchmark_student_group dbms_sql.varchar2_table,
strand dbms_sql.varchar2_table,
score dbms_sql.number_table,
percentile25 dbms_sql.number_table,
percentile75 dbms_sql.number_table
);

v_rec student_rec_typ;
v_bigstr varchar2(10000);
v_loop_cntr pls_integer := 0;
begin

v_bigstr :=
'select
last_analyzed a,
lag(table_name) over (order by table_name) b,
tablespace_name c,
cluster_name d,
num_rows e,
blocks,
empty_blocks
from
user_tables';


open c_percentile for v_bigstr;
loop
v_loop_cntr := v_loop_cntr + 1;
fetch c_percentile into
v_rec.time_series(v_loop_cntr),
v_rec.year_level(v_loop_cntr),
v_rec.benchmark_student_group(v_loop_cntr),
v_rec.strand(v_loop_cntr),
v_rec.score(v_loop_cntr),
v_rec.percentile25(v_loop_cntr),
v_rec.percentile75(v_loop_cntr);

exit when c_percentile%notfound;
end loop;
close c_percentile;

dbms_output.enable(1000000);
dbms_output.put_line(to_char(v_rec.year_level.count)||' records processed.');
for i in 1 .. v_rec.time_series.count loop
dbms_output.put_line(v_rec.time_series(i)||' '||v_rec.year_level(i)||' '||v_rec.benchmark_student_group(i));
end loop;

forall indx in v_rec.time_series.first .. v_rec.time_series.last
insert into
cdw_tmp_percentile (
time_series,
year_level,
benchmark_student_group,
strand,
score,
percentile25,
percentile75)
values
(v_rec.time_series(indx),
v_rec.year_level(indx),
v_rec.benchmark_student_group(indx),
v_rec.strand(indx),
v_rec.score(indx),
v_rec.percentile25(indx),
v_rec.percentile75(indx));
end;



Tom Kyte
May 24, 2005 - 7:34 am UTC

scalar subqueries for example:


tkyte@ORA8IW> select (select count(*) from dual ) c from dual;

C
----------
1

tkyte@ORA8IW> begin
2 for x in (
3 select (select count(*) from dual ) c from dual
4 ) loop
5 null;
6 end loop;
7 end;
8 /
select (select count(*) from dual ) c from dual
*
ERROR at line 3:
ORA-06550: line 3, column 9:
PLS-00103: Encountered the symbol "SELECT" when expecting one of the
following:
( - + mod not null others <an identifier>
<a double-quoted delimited-identifier> <a bind variable> avg
count current exists max min prior sql stddev sum variance
execute forall time timestamp interval date
<a string literal with character set specification>
<a number> <a single-quoted SQL string>
ORA-06550: line 3, column 37:
PLS-00103: Encountered the symbol "C" when expecting one of the following:
) , and or





But why isn't this code just

"insert into select ....."??? why have code at all?

Limit

A reader, June 30, 2006 - 9:59 pm UTC

Tom,
Is there any limit on dbms_sql.varchar2 table, for number of bytes/characters?
Thanks


Tom Kyte
July 01, 2006 - 7:50 am UTC

on the width - yes.
on the height - not any limit you would ever hit (2 billion rows)

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.