thanks Connor
Tushar, March 05, 2016 - 5:21 pm UTC
I added one dbms_output line to your code but getting error. Don't understand how to process the bulk collected value
if l_row_count > 0 then
dbms_output.put_line('Fetched '||l_row_count||' rows');
for i in 1 .. l_colCnt loop
dbms_sql.column_value( l_theCursor, i, l_col_array(i) );
dbms_output.put_line('col_value'||l_col_array(i));
end loop;
end if;
March 06, 2016 - 4:16 am UTC
l_col_array is an array of arrays,
so
l_col_array(1) represents column #1
l_col_array(2) represents column #2
and so forth.
So
l_col_array(1)(1) represents the first value fetched for column 1
l_col_array(1)(2) represents the first value fetched for column 2
l_col_array(1)(3) represents the first value fetched for column 3
etc
Hope this helps.
However, if you want to extract the actual data ...
Norman Dunbar, November 30, 2018 - 12:59 pm UTC
Hi Connor,
I'm late to the party again.
I used the code in the original example to do some dynamic SQL with bulk collect. I kept getting duplicate output. Not good.
The fix is to keep a running count of rows returned so far, and offset the 'i' in DBMS_SQL.COLUMN_VALUE, as follows:
set serverout on size 999999
declare
p_query varchar2(32767) := 'select * from emp';
type column_array is table of DBMS_SQL.VARCHAR2_TABLE index by pls_integer;
l_col_array column_array;
l_empty_table DBMS_SQL.VARCHAR2_TABLE;
l_theCursor integer default dbms_sql.open_cursor;
l_columnValue varchar2(4000);
l_status integer;
l_row_count integer;
l_descTbl dbms_sql.desc_tab;
l_colCnt number;
l_fetch_size int := 10;
n number := 0;
l_total_rows number := 0;
begin
dbms_sql.parse( l_theCursor, p_query, dbms_sql.native );
dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl );
for i in 1 .. l_colCnt loop
l_col_array(i) := l_empty_table;
dbms_sql.define_array(l_theCursor, i, l_col_array(i), l_fetch_size, 1);
end loop;
l_status := dbms_sql.execute(l_theCursor);
loop
l_row_count := dbms_sql.fetch_rows(l_theCursor);
if l_row_count > 0 then
dbms_output.put_line('Fetched '||l_row_count||' rows');
for i in 1 .. l_colCnt loop
--l_col_array(i).delete;
dbms_sql.column_value( l_theCursor, i, l_col_array(i) );
end loop;
-- Display the data.
for i in 1 .. l_row_count loop
for j in 1 .. l_colCnt loop
-- We need to offset the row index to account for previous batches
-- otherwise we get duplicate data. Ask me how I know!
dbms_output.put_line(l_col_array(j)(i + l_total_rows));
end loop;
end loop;
end if;
exit when l_row_count < l_fetch_size;
-- And adjust the row offset.
total_rows := total_rows + l_row_count;
end loop;
DBMS_SQL.CLOSE_CURSOR(l_theCursor);
end;
/
(Sorry about the formatting, it got eaten by the review submission process!)
HTH
Cheers,
Norm.
Formatting fixed. Apologies.
Norman Dunbar, November 30, 2018 - 1:01 pm UTC
Sorry, I can't read either. I fixed the formatting. (Sigh!)
set serverout on size 999999
declare
p_query varchar2(32767) := 'select * from emp';
type column_array is table of DBMS_SQL.VARCHAR2_TABLE index by pls_integer;
l_col_array column_array;
l_empty_table DBMS_SQL.VARCHAR2_TABLE;
l_theCursor integer default dbms_sql.open_cursor;
l_columnValue varchar2(4000);
l_status integer;
l_row_count integer;
l_descTbl dbms_sql.desc_tab;
l_colCnt number;
l_fetch_size int := 10;
n number := 0;
l_total_rows number := 0;
begin
dbms_sql.parse( l_theCursor, p_query, dbms_sql.native );
dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl );
for i in 1 .. l_colCnt loop
l_col_array(i) := l_empty_table;
dbms_sql.define_array(l_theCursor, i, l_col_array(i), l_fetch_size, 1);
end loop;
l_status := dbms_sql.execute(l_theCursor);
loop
l_row_count := dbms_sql.fetch_rows(l_theCursor);
if l_row_count > 0 then
dbms_output.put_line('Fetched '||l_row_count||' rows');
for i in 1 .. l_colCnt loop
--l_col_array(i).delete;
dbms_sql.column_value( l_theCursor, i, l_col_array(i) );
end loop;
-- Display the data.
for i in 1 .. l_row_count loop
for j in 1 .. l_colCnt loop
-- We need to offset the row index to account for previous batches
-- otherwise we get duplicate data. Ask me how I know!
dbms_output.put_line(l_col_array(j)(i + l_total_rows));
end loop;
end loop;
end if;
exit when l_row_count < l_fetch_size;
-- And adjust the row offset.
total_rows := total_rows + l_row_count;
end loop;
DBMS_SQL.CLOSE_CURSOR(l_theCursor);
end;
/
December 03, 2018 - 3:29 am UTC
Thanks for the update. I've replaced our original answer with yours.
Awesome example of using dbms_sql with bulk collect
A reader, February 20, 2019 - 10:20 am UTC
Great work ..please keep doing it.
Thanks a lot.
February 20, 2019 - 11:51 am UTC
You're welcome
jron, June 14, 2019 - 10:03 am UTC
<more code>
loop
l_row_count := dbms_sql.fetch_rows(l_theCursor);
if l_row_count > 0 then
dbms_output.put_line('Fetched '||l_row_count||' rows');
for i in 1 .. l_colCnt loop
--l_col_array(i).delete;
dbms_sql.column_value( l_theCursor, i, l_col_array(i) );
end loop;
-- Display the data.
for i in 1 .. l_row_count loop
for j in 1 .. l_colCnt loop
-- We need to offset the row index to account for previous batches
-- otherwise we get duplicate data. Ask me how I know!
dbms_output.put_line(l_col_array(j)(i + l_total_rows));
end loop;
end loop;
end if;
exit when l_row_count < l_fetch_size;
-- And adjust the row offset.
total_rows := total_rows + l_row_count;
end loop;
<more code>
Assuming a generic procedure/function will be created based on above code, if there are a few millions of records to be printed (e.g., in a file) then that means all records will be loaded in the array once the last batch of rows are retrieved, right? Wouldn't it be better to refresh the array before retrieving another set of records?
<more code>
loop
-- define_array in the loop
for i in 1 .. l_colCnt loop
l_col_array(i) := l_empty_table;
dbms_sql.define_array(l_theCursor, i, l_col_array(i), l_fetch_size, 1);
end loop;
l_row_count := dbms_sql.fetch_rows(l_theCursor);
if l_row_count > 0 then
for i in 1 .. l_colCnt
loop
dbms_sql.column_value(l_theCursor, i, l_col_array(i));
end loop;
-- Display the data.
for i in 1 .. l_row_count
loop
v_Separator := '';
for j in 1 .. l_colCnt
loop
-- no need to offset row index
utl_file.put(v_Separator || l_col_array(j)(i));
v_Separator := p_Separator;
end loop;
utl_file.put_line('');
l_total_rows := l_total_rows + l_row_count;
end loop;
end if;
exit when l_row_count < l_fetch_size;
end loop;
<more code>
June 18, 2019 - 3:22 am UTC
Agreed. Depends on what you want to achieve here, ie, get the entire result set, or loop through it and discard processed data.