массовый сбор в oracle

#oracle #plsql #oracle11g

#Oracle #plsql #oracle11g

Вопрос:

Как запросить массовый сбор? Если, например, у меня есть

 select name 
bulk collect into namesValues 
from table1
  

где namesValues dbms_sql.varchar2_table .

Теперь у меня есть еще одна таблица XYZ, которая содержит

  name   is_valid
  v
  h
  

Я хочу обновить is_valid до ‘Y’, если имя находится в таблице1, иначе ‘N’. Таблица 1 содержит 10 миллионов строк. После массового сбора я хочу выполнить

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

Как запросить значение namesValue? Или есть другой вариант. Таблица 1 не имеет индекса.
пожалуйста, помогите.

Комментарии:

1. Смотрите: download.oracle.com/docs/cd/E11882_01/server.112/e26088 /… , особенно примеры, связанные оттуда.

Ответ №1:

Как Том Кайт (Oracle Corp . Вице-президент) говорит:

Моя мантра, которую я буду придерживаться, спасибо вам большое, это:

Вы должны сделать это в одном операторе SQL, если это вообще возможно.

Если вы не можете сделать это в одной инструкции SQL, сделайте это в PL / SQL.

Если вы не можете сделать это в PL / SQL, попробуйте хранимую процедуру Java.

Если вы не можете сделать это на Java, сделайте это во внешней процедуре C.

Если вы не можете сделать это во внешней подпрограмме C, возможно, вам стоит серьезно подумать о том, зачем вам это нужно…

думайте в наборах…

узнайте все, что нужно знать о SQL…

Вы должны выполнить обновление в SQL, если можете. Если для этого вам нужно добавить индекс, то это может быть предпочтительнее перебора коллекции, заполненной с помощью МАССОВОГО СБОРА.

Если, однако, это какое-то назначение…. Вы должны указать его как таковой, но вот как вы это сделаете.

Я предположил, что ваш сервер БД не способен хранить 10 миллионов записей в памяти, поэтому вместо МАССОВОГО сбора всех 10 миллионов записей за один раз я поместил МАССОВЫЙ СБОР в цикл, чтобы уменьшить ваши накладные расходы на память. Если это не так, вы можете опустить цикл массового сбора.

 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;
/
  

В зависимости от ваших размеров сегмента отката и т. Д. возможно, вы захотите выполнить промежуточные коммиты в цикле массового сбора, но имейте в виду, что тогда вы не сможете откатить эти изменения. Я намеренно не добавил к этому никаких коммитов, чтобы вы могли выбрать, куда их поместить в соответствии с вашей системой.

Вы также можете захотеть изменить размер константы c_bulk_limit в зависимости от доступных вам ресурсов.

Ваше обновление все равно вызовет проблемы, если таблица xyz большая, а в столбце name нет индекса.

Надеюсь, это поможет…

Ответ №2:

«Таблица 1 не имеет индекса».

Ну, вот тут-то и кроется ваша проблема. Почему бы и нет? Поместите индекс на TABLE1.NAME и используйте обычное ОБНОВЛЕНИЕ SQL для изменения данных в XYZ.

Попытка решить эту проблему с помощью массового сбора — неправильный подход.