#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.
Попытка решить эту проблему с помощью массового сбора — неправильный подход.