Opa, algumas obs finais, mais para referência de quem for ler a thread : a. via de regra um dos grandes overheads da Deleção de registros é que é Inescapável o fato de se gerar UNDO e um REDO : isso decorre dos ditames da teoria de bancos de dados relacionais, que DEMANDA que TODA e QUALQUER operação seja reversível/passível de ser cancelada (daí o UNDO) ** e ** que também seja Recuperável (daí o REDO, que vai ser usado para recuperar os datafiles em caso de crash).... A maneira ** única ** de remover dados gerando o mínimo possível de UNDO e REDO (quase zero, mas não é zero por causa das tabelas internas do RDBMS) seria mesmo, depois de se ter salvo numa outra tabela os registros que se quer preservar, se fazer o ** TRUNCATE ** da tabela, que a esvazia de uma vez e não é uma Operação reversível, portanto não precisa gerar redo/undo para todos os dados da tabela, E só então se carregar de volta os dados a serem mantidos, preferencialmente com INSERT /+ APPEND */ para que a Inserção em si gere também o mínimo de redo (UNDO naturalmente quase não é gerado por um INSERT, pois Obviamente para se desfazer um INSERT é só 'zerar' a informação que está in place)... O que quero que fique ** CLARO ** aqui é que quem está poupando recursos aqui é o *** TRUNCATE **, e não o INSERT /* APPEND */, ok ??? Claro que o INSERT /*+ APPEND */ poderia ajudar bastante no processo de gerar uma "tabela-cópia" com os dados a serem mantidos, e que Ajudaria Muito também na hora de inserir os dados de volta depois do TRUNCATE, mas se não fosse possível o APPEND-mode por qquer motivo, até se poderia fazer a salva dos dados e a carga de volta após o TRUNCATE com INSERTs normais , export+import, sql*loader ou o que fosse.... NO SEU CASO, entendo que o que está te impedindo é a impossibilidade de janela de manutenção permitindo o acesso exclusivo requerido pelo TRUNCATE e pela salva/carga de dados antes e depois dele, e não o APPEND em si, ok ???? b. provavelmente vc não deve ter os recursos para tal, mas que fique REGISTRADO que via de regra se ter as múltiplas sessões "paralelas" fazendo um DELETE direto nos ROWIDs necessários, SEM cursor, é mais performático do que o fazer dentro de cursor , seja com ou sem bulk processing.... No seu caso, estou Assumindo que isso não será possível por causa de não ter espaço suficiente para crescer a área de ROLLBACK/UNDO para poder conter todo o undo gerado pelo DELETE, então por isso vc vai fazer um LOOP , ie, para poder comitar de tantos em tantos mils registros : isso implica em OVERHEAD, já que cada COMMIT implica em checkpoint, término de transação e consequente flush de redo, mas se vc não tem o necessário para a opção de maior performance, então não tem, que sseja assim...
c. os limites específicos (ie, quantas sessões simultâneas vc vai ter, qual será o LIMIT para o seu array pro BULK COLLECT e pro FORALL, etc) vai ser determinado pelo seu ambiente , mas eu diria pra vc TESTAR, numa máquina/ambiente/hardware o mais parecidos possíveis com a sua prod, a possibilidade de umas três sessões simultâneas , LIMIT de 1000 ou 2000 e COMMIT a cada 100 mil registros - esses números são baixos, são números que são razoáveis para a maioria dos servidores de produção que tenho visto, são os mais prováveis de não dar probs.... Aos poucos vc pode testar a chance de Ligeiros e paulatinos aumentos : não queira logo de cara sair jogando lá em cima esses números que quanto mais vc sobe mais alta a chance de vc esbarrar nos limites, seja do hardware, do ambiente OU mesmo do PL/SQL em si, como eu disse numa thread recente d. no instante em que vc bypassa o processamento "normal" e faz DML via cursor de usuário, é vc que fica responsável pelo tratamento de erros : assim, não deixe de pesquisar e incluir na sua rotina uma cláusula SAVE EXCEPTION no FORALL que vai montar o array com os ROWIDs a se deletar, para que nos registros que der erro/conflito por causa de contraint/whatever vc não perca a rastreabilidade deles - por mais que a gente verifique e verifique, sempre acaba passando algum... https://heliodias.wordpress.com/2010/01/07/best-way-to-delete-millions-rows-from-hundred-millions-table/ tem uns exemplinhos e discussão geral , e também se vc pesquisar em http://asktom.oracle.com vc acha boas refs e. o MAIS IMPORTANTE para a performance : além de usar corretamente o processamento por array/bulk, é FUNDAMENTAL que a query do cursor principal, que busca os registros a se deletar, esteja Absolutamente Otimizada - veja Direitinho quais índices vc tem para ter a melhor condição de filtro se for o caso, veja (já que afaik vc terá uma tabela com os registros a preservar) se é interessante ao invés de se fazer um JOIN da tabela com dados a preservar com a tabela real vc ao invés fazer um ANTIJOIN ou um semijoin (vide http://www.dbspecialists.com/files/presentations/semijoins.html) .... f. como eu disse em msgs anteriores, vc VAI "jogar no panelão" todos os recursos que puder, ie, vai dar o Máximo Possível pras sessões que estão fazendo os DELETEs : isso implica entre outras coisas de setar workarea_size_policy para MANUAL e dar o máximo possível de PGA / SGA pra elas (mas num volume que ABSOLUTAMENTE não interfira com as outras!!), E schedular as sessões de delete para o horário com MENOR CONCORRÊNCIA POSSÍVEL g. e nem preciso dizer : para vc poder acompanhar a performance do seu programa, é Imperativo que vc o INSTRUMENTE, ie, faça ele emitir informação de volumes e tempos periodicamente - eu costumo pra isso usar a built-in DBMS_APPLICATION_INFO , que coloca msgs curtas nas colunas da V$SESSION, e aí as acompanho a partir de outra sessão, tipo : .... open crow; ...loop fetch c bulk collect into brec limit 20000; DBMS_APPLICATION_INFO.SET_CLIENT_INFO('Vou entrar no loop em:' || to_char(sysdate, 'hh24:mi:ss')); forall vloop in 1 .. brec.count ---- SAVE EXCEPTIONS delete from big_table where rowid = brec(vloop); exit when crow%notfound; commit; ... dbms_application.set_client_info('Bulk completo em ' || to_char(sysdate, 'hh24:mi:ss') || ',linhas lidas=' || v_qtd_linhas); ... []s Chiappa