开发者

bulk collect in oracle

How to query bulk collection? If for example I have

select name 
bulk collect into namesValues 
开发者_StackOverflow中文版from table1

where namesValues is dbms_sql.varchar2_table.

Now, I have another table XYZ which contains

 name   is_valid
  v
  h

I want to update is_valid to 'Y' if name is in table1 else 'N'. Table1 has 10 million rows. After bulk collecting I want to execute

update xyz 
set is_valid ='Y' 
where name in namesValue.

How to query namesValue? Or is there is another option. Table1 has no index. please help.


As Tom Kyte (Oracle Corp. Vice President) says:

My mantra, that I'll be sticking with thank you very much, is:

You should do it in a single SQL statement if at all possible.

If you cannot do it in a single SQL Statement, then do it in PL/SQL.

If you cannot do it in PL/SQL, try a Java Stored Procedure.

If you cannot do it in Java, do it in a C external procedure.

If you cannot do it in a C external routine, you might want to seriously think about why it is you need to do it…

think in sets...

learn all there is to learn about SQL...

You should perform your update in SQL if you can. If you need to add an index to do this then that might be preferable to looping through a collection populated with BULK COLLECT.

If however, this is some sort of assignment.... You should specify it as such but here's how you would do it.

I have assumed that your DB server does not have the capacity to hold 10 million records in memory so rather than BULK COLLECTing all 10 million records in one go I have put the BULK COLLECT into a loop to reduce your memory overheads. If this is not the case then you can omit the bulk collect loop.

DECLARE
   c_bulk_limit CONSTANT PLS_INTEGER := 500000;
   --
   CURSOR names_cur
   IS
      SELECT name
        FROM table1;
   --
   TYPE namesValuesType IS TABLE OF table1.name%TYPE
        INDEX BY PLS_INTEGER;
   namesValues namesValuesType;
BEGIN

   -- Populate the collection
   OPEN name_cur;
   LOOP
      -- Fetch the records in a loop limiting them 
      -- to the c_bulk_limit amount at a time
      FETCH name_cur BULK COLLECT INTO namesValues
      LIMIT c_bulk_limit;

      -- Process the records in your collection
      FORALL x IN INDICES OF namesValues
         UPDATE xyz
            SET is_valid ='Y'
          WHERE name = namesValue(x)
            AND is_valid != 'Y';  

      -- Set up loop exit criteria
      EXIT WHEN namesValues.COUNT < c_bulk_limit;
   END LOOP;
   CLOSE name_cur;

   -- You want to update all remaining rows to 'N'
   UPDATE xyz
      SET is_valid ='N'
    WHERE is_valid IS NULL; 

EXCEPTION
   WHEN others
   THEN
      IF name_cur%ISOPEN 
      THEN
         CLOSE name_cur;
      END IF;
      -- Re-raise the exception;
      RAISE;
END;
/

Depending upon your rollback segment sizes etc. you may want to issue interim commits within the bulk collect loop but be aware that you will not then be able to rollback these changes. I deliberately haven't added any COMMITs to this so you can choose where to put them to suit your system.

You also might want to change the size of the c_bulk_limit constant depending upon the resources available to you.

Your update will still cause you problems if the xyz table is large and there is no index on the name column.

Hope it helps...


"Table1 has no index."

Well there's your problem right there. Why not? Put an index on TABLE1.NAME and use a normal SQL UPDATE to amend the data in XYZ.

Trying to solve this problem with bulk collect is not the proper approach.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜