2015-08-22 2 views
0

Мне нужно обновить таблицу в oracle db как пакет из 10k.обновление 1 миллиона записей в DB оракула как партия 10k;

Я попытался это:

BEGIN 
     WHILE (true) LOOP 
     UPDATE TOP (10000) CUSTOMERS SET ACTIVE = 'N' WHERE ACTIVE='Y'; 
     IF sql%notfound THEN 
       EXIT; 
     END IF; 
     COMMIT; 
     END LOOP; 
END; 

Это не работает, как PLSQL не поддерживает верх.

Любые предложения?

+4

Зачем это делать партиями? (Oracle отлично способен обновлять записи 1M в одном заявлении) –

ответ

0

Если вы автоматически инкрементируется синтетический ID столбец CUSTOMERS таблицы, то вы можете просто использовать WHERE условие о том,

WHERE ID <= 10000 
+0

У меня нет синтетического идентификатора – user2875775

+0

... и запрос должен измениться для каждой партии –

1
UPDATE CUSTOMERS 
SET ACTIVE = 'N' 
WHERE ACTIVE='Y' 
    AND ROWNUM <= 10000; -- first 10k rows 
0

Добавление поверх ответа АРД, чтобы обработать все строки без изменения запроса для каждой партии:

Перед обновлением:

select count(1) from cust ; --1000000 

select distinct active from cust ; -- Y 

Обновление:

Begin 
for i in 1..100 
loop 
update cust set Active = 'N' where ACTIVE = 'Y' 
and rownum <= 10000; 
-- dbms_output.put_line ('i value : ' || i); 
commit; 
end loop; 
dbms_output.put_line ('All rows updated '); 
end; 

Выход:

All rows updated 

Statement processed. 

3.77 seconds 

После того, как:

select distinct active from cust ; -- N 
1

Ваш PL/SQL блок, используя предложение АРД, в должны быть завершены, как это (somce вы новичок в PL/SQL Я добавляю некоторые синтаксические предложения, которые вы могли бы быть заинтересованы в):

BEGIN 
     -- WHILE (TRUE) can be omitted: "loop ... end loop;" 
     -- already is an endless loop 
     LOOP 
     UPDATE CUSTOMERS 
      SET ACTIVE = 'N' 
     WHERE ACTIVE='Y' 
      AND rownum <= 1000; 
     exit when sql%notfound; -- notice that exit accepts "when condition" 
     --IF sql%notfound THEN -- you can avoid a if/endif by using "exit when" 
     -- EXIT; 
     -- END IF; 
     COMMIT; 
    END LOOP; 
    commit; -- you missed this commit for the last iteration 
END; 

Не соблазн поставить «commit» перед «exit when sql% notfound»: после «commit» sql% notfound всегда ложно, и ваш цикл будет действительно бесконечным.

Позвольте мне указать, что для обеспечения эффективности этот подход требует индексации столбца «ACTIVE»!

Если у вас нет индекса в столбце «active», каждое «обновление» будет принудительно перезапускать полное сканирование таблицы с самого начала, чтобы найти следующие 1000 записей, которые все еще необходимо обновить.

Этот другой подход, который я предлагаю, использует некоторые усовершенствованные функции PL/SQL, которые вы, как ученик, интересуетесь (rowid, «таблица», объемные выборки курсора и «forall»), и делает только одно сканирование таблицу, которая должна быть обновлена, поэтому (в случае отсутствия индексов) она работает лучше, чем предыдущий подход. Имейте в виду, что если у вас есть индексы, это медленнее (но использование фокусов, объемных сборок и доступа к рядам, это не так уж медленнее), но он может пригодиться в случаях, когда вещи сложнее (например: когда условие where должно получить доступ к данным из других таблиц, используя сложные объединения, которые не могут быть сделаны быстрее). Бывают случаи, когда «где» является настолько сложным и медленным, что вы действительно не хотите повторять его снова и снова, используя подход «where rownum < = 1000».

declare 
     type rowid_array is table of rowid; 

     ids rowid_array; 

     cursor cur is 
     select rowid as id 
     from CUSTOMERS 
     where ACTIVE='Y'; 

    begin 
     open cur; 
     loop 
     fetch cur bulk collect into ids limit 1000; 
     exit when ids.count = 0; 

     forall c in ids.first .. ids.last 
      update CUSTOMERS set ACTIVE='N'; 

     commit;  
     end loop; 
    end; 
Смежные вопросы