sexta-feira, 4 de agosto de 2017

Otimizando o INSERT com FORALL - Oracle

E aí galera,
bom vamos dar um exemplo de como carregar muitos registros de uma
forma mais rápida, sem ficar sobrecarregando a área de UNDO do Oracle.
Eu preciso colocar vários clientes em atraso em uma tabela de log.



DECLARE
  /*---------------------------------------------------------------------------------*\   
  | DECLARE: ATUALIZA CLIENTES EM COBRANÇA                     |   
  | DATA CRIAÇÃO:  26/06/2005                                                             |   
  | OBJETIVO: INCLUI A CARGA DO DIA NO ESTOQUE.                |   
  | ANALISTA: GLAUBER                                                                       |   
  | Projeto: Contatos Cobrança                                                                   |   
  |==================================================|   
  | ALTERAÇÃO:                                                                                     |    
  | DATA ALTERAÇÃO:                                                                         |   
  | ANALISTA:                                                                                         |   
  \*---------------------------------------------------------------------------------*/
  
  v_QTD_UPDATE NUMBER(9) := 0;

  CURSOR c_CLIENTES_DIA IS
    SELECT DIA.ID_CONT_COBRANCA,       DIA.CPF,
           DIA.NOME,                                             DIA.STATUSREGISTRO,
           DIA.DATAULTCOMANDO,                   DIA.DATAENTCENTRAL,
           DIA.DATAATRASO,                               DIA.VALOR,
           DIA.UF,                                                    DIA.SEGMENTO,
           DIA.CDPRODLIM,                                 DIA.SUBSPID,
           DIA.COLLECTIONSCOR                      DIA.INDPARCATRASO,
           DIA.QTPARCATRASO,                         DIA.CODCAMPANHA,
           DIA.PRIORIDADE,                               DIA.VLRVAR,
           DIA.TIMEZONE,                                   DIA.CEP,
           DIA.INDQUEBRAPROMESSA,                    DIA.INDCOMPOSICAO,          
      FROM TB_CLIENTES_COBRANCA DIA
     WHERE DIA.DATAATRASO > (SYSDATE-30);
      
  --AQUI EU CRIO UMA VARIÁVEL DO TIPO ARRAY, PARA FICAR IGUAL AO CURSOR
  TYPE ARRAY IS TABLE OF c_CLIENTES_DIA%ROWTYPE INDEX BY PLS_INTEGER;
  S_ARRAY ARRAY;


BEGIN
  dbms_output.put_line('|------------------------------------------------------------|');
  dbms_output.put_line('|Início do processo:' || to_char(SYSDATE, 'dd/mm/yyy hh:mm:ss'));
  dbms_output.put_line('|------------------------------------------------------------|');

  OPEN c_CLIENTES_DIA;

  LOOP
    FETCH c_CLIENTES_DIA BULK COLLECT
      INTO S_ARRAY LIMIT 5000; --AQUI EU LIMITO UM VALOR DE 5000 CLIENTES POR EXECUÇÃO
   
   --AQUI EU FALO PRA ELE SAIR DO LOOP QUANDO NÃO RETORNAR MAIS DADOS
   EXIT WHEN S_ARRAY.COUNT = 0;

    --LEMBRANDO QUE A TABELA DESTINO DEVERÁ TER MESMOS CAMPOS QUE ESTÃO NO CURSOR.
    FORALL i IN 1 .. S_ARRAY.COUNT
      INSERT INTO TB_CONTATOS_COBRANCA_LOG VALUES S_ARRAY (i);

    -- AQUI EU CONTO A QUANTIDADE DE REGISTROS INSERIDOS
    v_QTD_INSERT := v_QTD_INSERT + SQL%ROWCOUNT;
    -- AQUI EU LIMPO A VARIÁVEL PARA SER USADA NOVAMENTE E NÃO FICAR LIXO.
    S_ARRAY.DELETE;

    COMMIT;

  END LOOP;
  CLOSE c_CLIENTES_DIA;

  COMMIT;
  dbms_output.put_line('|Quantidade de registros retornados: ' ||  v_QTD_INSERT);

  dbms_output.put_line('|------------------------------------------------------------|');
  dbms_output.put_line('|Fim do processo:' || to_char(SYSDATE, 'dd/mm/yyyy hh:mm:ss'));
  dbms_output.put_line('|------------------------------------------------------------|');

EXCEPTION
  WHEN OTHERS THEN
    dbms_output.put_line('|------------------------------------------------------------|');
    dbms_output.put_line('| MENSAGEM ORACLE : ' || SQLERRM);
    dbms_output.put_line('|------------------------------------------------------------|');
    dbms_output.put_line('|Fim do processo:' || to_char(SYSDATE, 'dd/mm/yyyy hh:mm:ss'));
    dbms_output.put_line('|------------------------------------------------------------|');
END;


Blz galera, esse é um exemplo fácil para você criar um insert em lote, vlw
qualquer dúvida só postar nos comentários.