tags:

views:

74

answers:

3

Hi Oracle experts.

I'm having a little issue with a piece of bulk collect sql that I was hoping you could help out with.

With the following code:

declare
    cursor c1
    is
    select customer,product
    from products;

    type type_cust is table of products.customer%type;
    type type_prod is table of products.product%type;

    v_array_cust    type_cust;
    v_array_prod    type_prod;
begin
    open c1;
    loop
        fetch c1 
        into v_array_cust, v_array_prod
        limit 1000;

        exit when c1%notfound;

        for i in 1..v_array_cust.count
        loop
            --Do some processing here.
        end loop;
    end loop;
end;
/

The cursor c1 returns 53166 rows.

However, the code process 53000 rows and then ends. It seems that when going to retrieve the last 166 records there is some sort of failure.

Will the fetch return %notfound if it find's less than 1000 records? Should I move the exit to the end of the loop? (I am going to try this but it is deep in a piece of code that take 3 hours to get to the failure point.)

Thanks in advance.

+6  A: 

OK, well a better bit of googling than I'd already done gave me the answer you shouldn't use %notfound with limit.

Check here for an explanation.

gnuchu
+1 for finding the answer yourself.
Bob Jarvis
+1  A: 

Sorry to be the one to say it, but ack! cursors!...the way you've written this appears you've come from a linear programming background. Have you considered a set based solution to this?

M.E.
@M.E., that's a bit presumptuous since he doesn't specify what the "Do some processing here" bit does. It might be something that is impossible or inefficient to do within the SQL. Plus, he's fetching 1000 rows at a time into memory, which is usually a very efficient method for processing large sets of data in PL/SQL.
Jeffrey Kemp
A loop is a loop is a loop ;) Just making sure the question of 'do I really need a loop/cursor' was asked.
M.E.
I do come from a linear programming background. But this isn't my code - it's legacy code that I am supporting. Is there another option? I don't understand what you mean by 'set based solution'.
gnuchu
I would have to see what the 'some processing includes' but really simplified, linear is one record at a time while setbased would be doing all records (the entire 'set') simoultaneously. I'm guessing the original coder here had attempted the loop with all 50k records, but had to break it into 1000 record segments later on...but thats purely speculation. There is very few things that can't be done set based in a relational datamodel, so I would be surprised if this couldn't be rewritten to set-based logic...might see a pretty good speed improvement on the entire query too
M.E.
+2  A: 

Just for reference, here's one simple change to make the code run correctly:

open c1;
loop
    fetch c1 
    into v_array_cust, v_array_prod
    limit 1000;

    -- the count will be 0 if no rows were found, so this loop will do nothing.
    for i in 1..v_array_cust.count
    loop
        --Do some processing here.
    end loop;

    -- exit now if the last fetch got the last set of rows
    exit when c1%notfound;
end loop;
close c1;
Jeffrey Kemp
Thanks. I went with 'exit when v_array_cust.count=0' which works just as well.
gnuchu
yes, that'll work as well - doesn't make much difference, but your method would involve an extra unnecessary fetch from the cursor (but that's just me being picky)
Jeffrey Kemp