Bulk collect into Pro*C variables
Kalpesh Patel, March 16, 2001 - 1:36 pm UTC
This is really useful when someone doesn't want to use PL/SQL variables for bulk collect.
With this , we can do bulk collect into Pro*C variables and we can use them into our Pro*C code for some calculation or manipulation.
Thanks Tom, for such a useful information.
How about cursor with GROUP BY clause?
Julien Xu, February 05, 2004 - 3:09 am UTC
My cursor in Pro*C is:
SELECT BODY, ACCT, PRPRTY, SUM(NB_MEAS)
FROM TREAD
WHERE BATCH_NB = 75
GROUP BY BODY, ACCT, PRPRTY;
I define host arrays to receive the results. But after FETCHing, I find there is only one line in the arrays, all other cells are empty (0). Actually there should have >300 results.
If I remove that GROUP BY and that SUM(), the results are all FETCHed as expected. But I must need that SUM() and GROUP BY.
So why and how to fix this?
February 05, 2004 - 7:39 am UTC
if you run that query in sqlplus -- what are the results (how many rows) and show us the entire piece of PRO*C code, include things like "my version", "my platform"
Otherwise this question fits into the "my car won't start -- why?" category ;)
prefetch and host array
Pinguman, September 09, 2004 - 7:16 am UTC
Hi
Currently we are using Oracle 8.1.7.4 on Sun Solaris. We have some PRO*C codes which we would like to convert and use HOST ARRAYS. From your answer it seems that we dont need to modify the code and simply use PREFETCH option and the PRO*C will start using HOST ARRAYS. Am I correct?
For example if I have this
long int v_transation
cursor blah blah
EXEC SQL FETCH cur_cursor
into v_transation
This processes row by row, if I use PREFETCH 100 will this use HOST ARRAYS?
September 09, 2004 - 8:33 am UTC
correct -- it will do it "automagically"
this subroutine:
void process()
{
exec sql begin declare section;
int x;
exec sql end declare section;
EXEC SQL WHENEVER SQLERROR DO sqlerror_hard();
exec sql declare c cursor for select rownum from all_objects where rownum <= 1000;
exec sql open c; for( ;; )
{
exec sql whenever notfound do break; exec sql fetch c into :x;
}
exec sql whenever notfound do continue;
exec sql close c;
}
when executed without setting prefetch:
select rownum from
all_objects where rownum<=1000
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.03 0.03 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 501 0.23 0.33 8 4560 4 1000
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 503 0.26 0.36 8 4560 4 1000
shows the default is 2 -- with prefetch=100
select rownum from
all_objects where rownum<=1000
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 11 0.17 0.17 0 4075 4 1000
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 13 0.17 0.17 0 4075 4 1000
showing the reduced fetch calls...
are we talking about host arrays?
A reader, September 10, 2004 - 5:40 am UTC
Hi
If I understand correctly host arrays is not about fetching data is it? (from doc, With arrays, you manipulate an entire array with a single SQL statement)
For example, if I have to run 10 insert statements using host arrays I probably need one only.
Or host arrays is about both, fetching and number of stateents excuted?
September 10, 2004 - 9:17 am UTC
you can array fetch -- so to retrieve 1000 rows you could either
o fetch a row at a time, 1000 fetches OR
o fetch N rows at a time, 1000/N fetches (eg: n = 100, you'd fetch 10 times instead of 1000 times)
you can array insert/update/delete as well.
say you have 1000 rows to insert.
you batch them up into an array of 100 elements at a time.
You'll EXECUTE your insert 10 times, inserting 1000 rows.
so, array processing is a method to "batch things up"
drawbacks of prefetching?
Andrey, September 21, 2004 - 4:14 pm UTC
Tom,
What are the drawbacks of setting the array size too high?
Just preallocating too much memory on a client machine?
Any additional network traffic because of it?
Is your method any different performance-wise, with the following line?
EXEC SQL FOR :batch_limit FETCH db_batch_cursor_1 USING DESCRIPTOR select_des;
Thanks!
September 21, 2004 - 4:24 pm UTC
memory, choke points (hey, lets flood the network with 500megabytes right now), latency (why does it take 30 seconds to get that first row, I only care about 10 of them initially), etc.
Yes, you'll find diminishing marginal returns for large array sizes -- I have an analogy in Effective Oracle by Design with regards to a wheelbarrow, a big pile of bricks and two people wanting to pave a driveway (regards of Connor McDonald whom I believe developed the analogy in the first place as I recall...)
bringing over a brick at a time (row by row) is slow. lots of wait times, not processing anything.
bringing over the entire stack of bricks is fraught with danger (tipping over), and because the thing weighs so much, takes a long time. So, for a long time, wheelbarrow person works, paver sits. then paver works and wheelbarrow guy does nothing.
brining over a nice pile to get started and going back and forth and back and forth keeps things flowing nicely. no one gets stressed out, no one gets hurt.
But the parses...
Shawn, January 13, 2005 - 8:53 am UTC
I've been reviewing a database for sql effeciency. One of the items that I found was I had a large amount of sql (greater than 50%) parsing everytime it executes. At first I was alarmed at what was causing this, but now I'm not sure if I should be concerned or if it can be eliminated. We have a number of batch processes that call subroutines to perform specific processing. This is all written in Pro*C with this basic pattern...
void process(int v_someid)
{
exec sql begin declare section;
int x;
exec sql end declare section;
EXEC SQL WHENEVER SQLERROR DO sqlerror_hard();
exec sql declare c cursor for select some_columns from some_table where id = :id;
exec sql open c; for( ;; )
{
exec sql whenever notfound do break;
exec sql fetch c into :x;
}
exec sql whenever notfound do continue;
exec sql close c;
}
They are opening a cursor, processing all rows for a specific id fetching 20 at a time, and then closing the cursor. However, the batch process calls this routine once for every id and we process millions of id's a day. It appears that every time the subroutine is called, the statement is parsed. Should I be concerned about this from a performance stand point? Is there a different/better way to accomplish this task? Or, do I just have to live with the fact that it parses everytime and go on with life because it's not that big of a deal?
This is 7 terabyte instance of ORACLE 9.2 on HP-UX server (32 processor with 96GB of RAM)
January 13, 2005 - 9:14 am UTC
hold_cursor=yes release_cursor=no prefetch=20
with hold cursor no, release cursor yes, prefetch defaulted:
select user_id
from
t where user_id>:b0
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 10000 0.19 0.17 0 0 0 0
Execute 10000 0.53 0.35 0 0 0 0
Fetch 110000 2.50 2.41 1 120000 0 190000
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 130000 3.22 2.94 1 120000 0 190000
with hold_cursor=yes, release cursor = no, prefect=20
select user_id
from
t where user_id>:b0
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 10000 0.39 0.39 0 0 0 0
Fetch 20000 1.18 1.00 1 40000 0 190000
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 30001 1.57 1.40 1 40000 0 190000
Ask the coders to take a peek at the pro*c precompile options and see if any of them might be "interesting"
You have a big honking machine there, you probably try to be very scalable. Parsing is death to scaling.
The more you parse, the less you parse.....
(sounds counterintuitive but it is true -- the more you try to parse, the less you can parse, the lines get longer and longer and longer and longer....)
Thanks.
shawn, January 13, 2005 - 9:22 am UTC
"You have a big honking machine there, you probably try to be very scalable.
Parsing is death to scaling." That was my thought exactly, just didn't know how to get around it. I'll get back with the developers and see what we can get accomplished.
Giridhar, June 01, 2005 - 11:06 pm UTC
Hi Tom,
Would you please explain more on how the PREFETCH happens.
When we fetch data in a loop (without PREFETCH), we fetch one row for every iteration in the loop. If we use PREFETCH, oracle prefetches more than 1 record for us depending on the PREFETCH value.
As we use the LOOP, will oracle PREFETCH data once and ignore fetching till it iteration counter is equal to PREFETCH value.
Thanks in advance Tom.
Regards,
Giridhar
June 02, 2005 - 4:25 pm UTC
you explained it very well? what more need be said?
Thanks
Giridhar, June 05, 2005 - 12:47 am UTC
Thanks tom for your reply. I just wanted to confirm if my understanding of PREFETCH was correct.
Regards,
Giridhar
A reader, February 18, 2009 - 2:23 pm UTC
We have a job scheduler written in Pro*C, which runs in the background in the UNIX machine.
It's job is to pick up requests from a job_queue table and process the requests sequentially.
The requests can be to send to email, generate oracle report, run plsql code, run shell scripts, convert text files to pdf etc.
We need to make this job multithreaded to improve the performance.
What is the best approach to do this?
I was looking into the below approaches.
1) Multhreaded Applications in Pro*C
2) Host Arrays
Please let me know if there are any other approaches available?
February 18, 2009 - 4:06 pm UTC
why not erase the code and use dbms_scheduler.
it can run external things
has a history/audit trail built in
is already very multi-threaded
A reader, February 18, 2009 - 4:40 pm UTC
Thanks Tom, but we are in Oracle 9i !
February 18, 2009 - 5:46 pm UTC
use dbms_job and a java stored procedure to run host commands.
dbms_job can do upto 1,000 things at a time.
You'd don't get the features of the scheduler (better intervals/schedules and audit), but you can more easily do that (build your own audit) than attempt to make a multi-threaded pro*c app.
That would be my first choice in 9i - dbms_job.
I would not go multi-threaded in the C program, if I was to stick with the C code (and I would not), I would have it use AQ (advanced queues) and instead of you sticking just into a do it yourself queue (you'd have to figure out how to handle concurrency on that one - AQ does that for you) have the requestor put it into a real queue.
And then just start N of your C programs, each will attempt to dequeue a message - if you start 5 and there are 10 messages, each will get one of the first five, process it and then get the rest...
That would be 2nd choice.
A cheap way to do it would be to simply grab a row, fork/exec the program to deal with it.