Procedure com BULK COLLECT limitado.

23/03/2023

0

Boa tarde, estou tentando criar uma procedure utilizando o BULK COLLECT para uma melhor performance, porém está dando erro na linha 19,
Error: PLS-00394: wrong number of values in the INTO list of a FETCH statement
Line: 19
Text: FETCH c_TESTE BULK COLLECT INTO v_teste;

Poderiam me ajudar? Segue a procedure:

CREATE OR REPLACE PROCEDURE CONSULTA_ITENS_MOVTO
IS
   CURSOR c_TESTE IS
   SELECT
     M.DATAMOVTO, M.SEQMOVTO, M.CODIGOMARCAMAT, M.CODIGOMATINT, M.CODIGOLOCAL, M.QTDEITENSMOVTO, M.VALORITENSMOVTO
   FROM
     EST_ITENSMOVTO M
   WHERE
     M.DATAMOVTO BETWEEN '01-JAN-2019' AND '31-DEC-2019'
   ORDER BY
     M.DATAMOVTO, M.SEQMOVTO;

   TYPE r_TESTE IS TABLE OF EST_ITENSMOVTO%rowtype;
   v_teste   r_TESTE;

BEGIN
   OPEN c_TESTE;
   LOOP
       FETCH c_TESTE BULK COLLECT INTO v_teste;
       FORALL i IN 1..v_teste.COUNT
            INSERT INTO EST_TESTE(DATAMOVTO,SEQMOVTO,CODIGOMARCAMAT,CODIGOMATINT,CODIGOLOCAL,QTDEITENSMOVTO,VALORITENSMOVTO)
            VALUES (v_teste(i).DATAMOVTO,v_teste(i).SEQMOVTO,v_teste(i).CODIGOMARCAMAT,v_teste(i).CODIGOMATINT,v_teste(i).CODIGOLOCAL,v_teste(i).QTDEITENSMOVTO,v_teste(i).VALORITENSMOVTO);
         COMMIT;
   END LOOP;
   CLOSE c_TESTE;
END CONSULTA_ITENS_MOVTO;
Breno

Breno

Responder

Post mais votado

23/03/2023

Provavelmente o conjunto de colunas da tabela EST_ITENSMOVTO não está batendo com o número de colunas do cursor c_TESTE.

Você pode testar alterar a definição da variável de:

TYPE r_TESTE IS TABLE OF EST_ITENSMOVTO%rowtype;

Para:

TYPE r_TESTE IS TABLE OF c_TESTE%rowtype;

Arthur Heinrich

Arthur Heinrich
Responder

Mais Posts

24/03/2023

Breno

Provavelmente o conjunto de colunas da tabela EST_ITENSMOVTO não está batendo com o número de colunas do cursor c_TESTE.

Você pode testar alterar a definição da variável de:

TYPE r_TESTE IS TABLE OF EST_ITENSMOVTO%rowtype;

Para:

TYPE r_TESTE IS TABLE OF c_TESTE%rowtype;

Deu certo, muito obrigado!! Não tinha pensado nessa opção, anteriormente tentei fazer o TYPE utilizando a tabela de destino, mas não tinha dado certo, também.
Responder

24/03/2023

Arthur Heinrich

Boa tarde, estou tentando criar uma procedure utilizando o BULK COLLECT para uma melhor performance, porém está dando erro na linha 19,
Error: PLS-00394: wrong number of values in the INTO list of a FETCH statement
Line: 19
Text: FETCH c_TESTE BULK COLLECT INTO v_teste;

Poderiam me ajudar? Segue a procedure:

CREATE OR REPLACE PROCEDURE CONSULTA_ITENS_MOVTO
IS
   CURSOR c_TESTE IS
   SELECT
     M.DATAMOVTO, M.SEQMOVTO, M.CODIGOMARCAMAT, M.CODIGOMATINT, M.CODIGOLOCAL, M.QTDEITENSMOVTO, M.VALORITENSMOVTO
   FROM
     EST_ITENSMOVTO M
   WHERE
     M.DATAMOVTO BETWEEN '01-JAN-2019' AND '31-DEC-2019'
   ORDER BY
     M.DATAMOVTO, M.SEQMOVTO;

   TYPE r_TESTE IS TABLE OF EST_ITENSMOVTO%rowtype;
   v_teste   r_TESTE;

BEGIN
   OPEN c_TESTE;
   LOOP
       FETCH c_TESTE BULK COLLECT INTO v_teste;
       FORALL i IN 1..v_teste.COUNT
            INSERT INTO EST_TESTE(DATAMOVTO,SEQMOVTO,CODIGOMARCAMAT,CODIGOMATINT,CODIGOLOCAL,QTDEITENSMOVTO,VALORITENSMOVTO)
            VALUES (v_teste(i).DATAMOVTO,v_teste(i).SEQMOVTO,v_teste(i).CODIGOMARCAMAT,v_teste(i).CODIGOMATINT,v_teste(i).CODIGOLOCAL,v_teste(i).QTDEITENSMOVTO,v_teste(i).VALORITENSMOVTO);
         COMMIT;
   END LOOP;
   CLOSE c_TESTE;
END CONSULTA_ITENS_MOVTO;


Complementando a resposta anterior, referente à correção do erro, segue uma sugestão de boas práticas.

Seu código prevê que o dado seja copiado em blocos, uma vez que utiliza um loop, mas apresenta mais dois erros:

1 - Não prevê um critério de parada.

Um fetch após o último registro lido retorna 0 linhas, mas não aborta um loop/end loop. É necessário interromper o loop com uma checagem condicional, do tipo:

exit when v_teste.COUNT = 0;

2 - Não foi imposto um limite ao tamanho do fetch.

Caso o cursor retorne 1 bilhão de linhas, o banco tentará fazer o fetch de 1 bilhão de linhas de uma só vez. Isto não aumenta o desempenho e põe em risco a estabilidade da instância, seja pelo consumo excessivo de memória, tempo da transação ou consumo de undo.

O ideal é limitar o fetch, para garantir que as transações seja eficientes, mas pequenas e rápidas, evitando efeitos colaterais.

O objetivo do bulk collect/insert é minimizar o chaveamento de contexto entre a aplicação PL/SQL e o banco. A documentação sugere que utilizar blocos de 30 linhas pode ser suficiente, já que reduziria o chaveamento de contexto em aproximadamente 97%.

Eu costumo fazer uma estimativa do número de blocos afetados, utilizando um número aproximado de 100 blocos. Desta forma, em um insert como este, você preencherá completamente cerca de 99 blocos e o último terá algum espaço livre, que será preenchido pelo próximo insert. Isto faz com que o processamento duplicado de um bloco caia para cerca de 1% e você garante uma transação de 800 KB, que é bem pequena, rápida e libera os locks.

Ex.: Se o seu insert gera linhas de aproximadamente 150 bytes, o bloco utiliza 8 KB e a tabela foi configurada com PCT_FREE 10, por exemplo, cada bloco poderá conter: (8192 * 0.9)/150 ~49 linhas. Neste caso, eu utilizaria o fetch com um limite de 4900 ou 5000 linhas, para ficar redondo.

FETCH c_TESTE BULK COLLECT INTO v_teste limit 5000;
Responder

24/03/2023

Arthur Heinrich

BEGIN
  OPEN c_TESTE;
  LOOP
    FETCH c_TESTE BULK COLLECT INTO v_teste LIMIT 5000;
    EXIT WHEN v_teste.COUNT = 0;

    FORALL i IN 1..v_teste.COUNT
      INSERT INTO EST_TESTE
       (DATAMOVTO, SEQMOVTO, CODIGOMARCAMAT, CODIGOMATINT, CODIGOLOCAL,
        QTDEITENSMOVTO, VALORITENSMOVTO)
      VALUES
       (v_teste(i).DATAMOVTO, v_teste(i).SEQMOVTO, v_teste(i).CODIGOMARCAMAT, v_teste(i).CODIGOMATINT, v_teste(i).CODIGOLOCAL,
        v_teste(i).QTDEITENSMOVTO, v_teste(i).VALORITENSMOVTO);
    COMMIT;
  END LOOP;
  CLOSE c_TESTE;
END CONSULTA_ITENS_MOVTO;
Responder

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar