Olá, leitores!
Voltei com mais uma dica para vocês. Dessa vez, o assunto é FireDAC!
Se você já utiliza essa tecnologia de conexão e insere grandes volumes de dados na sua aplicação, convido-o a ler este artigo!
Quem um dia já não precisou inserir 5, 10, 50 mil registros no banco de dados de uma vez só? Essa necessidade é relativamente comum em ambientes de migração, ambientes centralizados, tabelas associativas (de junção) ou até mesmo por conta da regra de negócio do cliente.
A solução pode parecer um tanto quanto óbvia: inserir um registro de cada vez dentro de um loop. Porém, não é tão óbvia assim. O FireDAC traz um recurso muito útil, chamado ArrayDML, que permite a execução de instruções SQL em lote, reduzido radicalmente o tempo decorrido com a operação.
Exemplo
Para exemplificar este recurso, gerei um mock com 10 mil linhas em formato CSV, composto pelos campos “ID”, “Name”, “Email”, “Company”, “Occupation”, “City” e “University”. A intenção é percorrer o arquivo e inserir cada linha em uma tabela de um banco de dados local. Para isso, utilizaremos um componente TFDQuery
com a seguinte instrução SQL:
1 2 |
INSERT INTO Tabela (ID, Name, Email, Company, Occupation, City, University) VALUES (:ID, :Name, :Email, :Company, :Occupation, :City, :University) |
A princÃpio, farÃamos essa funcionalidade de uma forma, digamos, “tradicional”, executando um INSERT para cada linha, conforme o código a seguir. Apenas como fator de parâmetro, adicionei duas linhas recebendo a data e hora atual (Now
) para calcular o tempo decorrido na operação:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
var lStringListFile: TStringList; lStringListLine: TStringList; lLine: string; lStart, lEnd: TDateTime; begin // Armazena o inÃcio da operação lStart := Now; // TStringList que carrega todo o conteúdo do arquivo lStringListFile := TStringList.Create; // TStringList que carrega o conteúdo da linha lStringListLine := TStringList.Create; try // Carrega o Mock lStringListFile.LoadFromFile('MOCK.csv'); for lLine in lStringListFile do begin lStringListLine.StrictDelimiter := True; // TStringList recebe o conteúdo da linha atual lStringListLine.CommaText := lLine; // Preenche os parâmetros da Query FDQuery.ParamByName('ID').AsString := lStringListLine[0]; FDQuery.ParamByName('Name').AsString := lStringListLine[1]; FDQuery.ParamByName('Email').AsString := lStringListLine[2]; FDQuery.ParamByName('Company').AsString := lStringListLine[3]; FDQuery.ParamByName('Occupation').AsString := lStringListLine[4]; FDQuery.ParamByName('City').AsString := lStringListLine[5]; FDQuery.ParamByName('University').AsString := lStringListLine[6]; // Executa o comando INSERT FDQuery.ExecSQL; end; finally lStringListLine.Free; lStringListFile.Free; end; // Armazena o fim da operação lEnd := Now; // Exibe o tempo total da operação ShowMessage('Time elapsed: ' + FormatDateTime('hh:nn:ss:zzz', lEnd - lStart)); end; |
Ao executar essa rotina, recebemos o seguinte resultado do tempo:
Em seguida, faremos a mesmo operação, porém, utilizando o ArrayDML. Para isso, é necessário apenas 3 ajustes:
- Indicar o tamanho do “lote” (ou array), no qual equivale à quantidade de inserções que serão realizadas;
- Usar métodos de preenchimento de parâmetros no “plural” (por exemplo,
AsStrings
ao invés deAsString
); - Chamar o método
Execute
do componenteTFDQuery
, informando a quantidade de inserções desejadas.
Essas três alterações no código são apresentadas abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
var lStringListFile: TStringList; lStringListLine: TStringList; lCounter: integer; lStart, lEnd: TDateTime; begin // Armazena o inÃcio da operação lStart := Now; // TStringList que carrega todo o conteúdo do arquivo lStringListFile := TStringList.Create; // TStringList que carrega o conteúdo da linha lStringListLine := TStringList.Create; try // Carrega o Mock lStringListFile.LoadFromFile('MOCK.csv'); // Configura o tamanho do array de inserções FDQuery.Params.ArraySize := lStringListFile.Count; for lCounter := 0 to Pred(lStringListFile.Count) do begin lStringListLine.StrictDelimiter := True; // TStringList recebe o conteúdo da linha atual lStringListLine.CommaText := lStringListFile[lCounter]; // Preenche os parâmetros em cada posição do array FDQuery.ParamByName('ID').AsStrings[lCounter] := lStringListLine[0]; FDQuery.ParamByName('Name').AsStrings[lCounter] := lStringListLine[1]; FDQuery.ParamByName('Email').AsStrings[lCounter] := lStringListLine[2]; FDQuery.ParamByName('Company').AsStrings[lCounter] := lStringListLine[3]; FDQuery.ParamByName('Occupation').AsStrings[lCounter] := lStringListLine[4]; FDQuery.ParamByName('City').AsStrings[lCounter] := lStringListLine[5]; FDQuery.ParamByName('University').AsStrings[lCounter] := lStringListLine[6]; end; // Executa as inserções em lote FDQuery.Execute(lStringListFile.Count, 0); finally lStringListLine.Free; lStringListFile.Free; end; // Armazena o fim da operação lEnd := Now; // Exibe o tempo total da operação ShowMessage('Time elapsed: ' + FormatDateTime('hh:nn:ss:zzz', lEnd - lStart)); end; |
Agora, observem só o tempo total com o ArrayDML:
Menos de 1 segundo?!
Na verdade, menos de meio segundo! Com o ArrayDML, as 10 mil inserções foram executadas absurdamente mais rápido!
Para evidenciar essa diferença, fiz questão de gravar um pequeno vÃdeo da execução deste código. Neste vÃdeo, executei a aplicação em outro computador, então os tempos estão um pouco diferentes.
Pessoal, é a primeira vez que me “aventuro” na gravação de vÃdeos, portanto, não reparem na qualidade, ok? =D
Fico por aqui, leitores!
Um abraço e até breve.
Muito bom! Eu mesmo ja passei por essa necessidade de incluir vários registros de uma unica vez e na época não conhecia esse fantástico recurso do FireDAC… Parabéns pelo conteúdo…
Obrigado pelo comentário, Vinicius!
Eu também tive essa mesma necessidade, mas na época utilizava o DBX. Já migrei boa parte dos meus projetos para FireDAC justamente para tirar proveito de recursos com este. 🙂
Um abraço!
Parabéns mestre Celestino, realmente quando migrei as replicaçoes para arraydml a carga caiu absurdamente, caso de carga de 5 a 10 minutos cair para 10 segundos.
Show, forte abraço
Eu que agradeço, Panda!
A ideia desse artigo partiu de uma necessidade que você tinha, lembra? 🙂
O recurso realmente é fantástico!
Abraço!
Boa tarde André,
Mais uma vez nos surpreendendo com seus artigos, muito bom, parabéns e muito obrigado por doar seu tempo para compartilhar seus conhecimentos.
Grande abraço.
Opa, muito obrigado, Daniel!
Agradeço novamente por sempre estar acompanhando as publicações do blog!
Abração!
Parabéns, André!
Bem interessante esse recurso.
Abraços.
Obrigado, Olivetti!
Gostei bastante deste recurso do FireDAC! 😉
“praticamente 7 vezes mais rápido!” Na verdade 14 vezes mais rápido.
Tem razão, Marcelo! Errei na proporção, rsrs.
Abraços!
Muito interessante esse recurso.
Porém me surgiu uma duvida. Se nessas 10 mil inserções algumas delas falharem, irá comprometer todas as outras ?
Boa noite, Everton!
As falhas nas inserções podem ser capturadas no evento OnExecuteError do componente TFDQuery. Neste evento, é possÃvel definir 3 possÃveis comportamentos:
– aeOnErrorUndoAll: A operação é interrompida na primeira falha e todas as inserções são desfeitas;
– aeUpToFirstError: A operação é interrompida na primeira falha e as inserções já realizadas são salvas;
– aeCollectAllErrors: Todas as inserções são realizadas, exceto as que retornaram falha. Neste caso, é possÃvel rastrear o Ãndice de cada inserção que falhou.
Grande abraço!
Ótimo artigo!
Me surgiu uma dúvida, existe uma Quantidade máxima de registros para este recurso?
Ótima pergunta, Eduardo!
Segundo a documentação da Embarcadero, a quantidade máxima de registros depende do banco de dados utilizado. Cada SGBD possui um tamanho máximo especÃfico de pacote de dados.
De qualquer forma, caso haja um estouro do limite, é possÃvel capturar essa exceção no evento OnExecuteError e exigir uma quantidade menor de inserções.
Abraço!
Excelente dica.
Eu não conhecia o recurso e fez uma diferença enorme na importação de dados.
Obrigado, que Deus abençoe e que sempre possa compartilhar essas dicas que nos ajudam muito.
Agradeço pelo comentário, Jefferson.
Fico feliz que tenha gostado deste recurso!
Abração!
Muito bom, obrigado, não conhecia esse recurso, você sempre dá dicas maravilhosas, mas fiquei com uma dúvida.
Tenho uma rotina de importação de dados, na qual eu exporto e importo nas filiais vários dados, hoje utilizo da seguinte forma, no TFDquery crio um “select * from Tabela where id = :id”, faço uma verificação se a data atual é menor que data do arquivo de importação, se for, dou um TFDQuery.edit, se não existir, dou um Insert,
Essa função irá ajudar em algo nessa rotina? ou teria que mudar para UPDATE e INSERT via comando e usar o EXECSQL?
Desde já, agradeço a atenção.
Olá, Marcos! Ótima pergunta.
Para tirar proveito do recurso de ArrayDML do FireDAC, é necessário escrever os comandos SQL (Insert e Update), justamente por causa do
ParamByName
. Portanto, no seu caso, você teria que modificar a sua lógica para executar oExecSQL
ao invés deInsert/Edit
.Abraço!
André, também estou com a mesma dúvida do Marcos Pontes, tem como vc descrever um exemplo?
Não sei se usei corretamente os comandos aqui, mas a performance não melhorou muito usando quando preciso, verificar consultando se o registro já existe ou não.
Desde já agradeço.
Olá, Dirceu, tudo bem?
Se possÃvel, envie o seu código para “[email protected]” para que eu possa analisá-lo.
Abraço!
Excelente conteúdo e de fácil entendimento André, está de parabéns! Continue assim 🙂
Somente um detalhe caso alguém passe o que passei e sofri um pouquinho até descobrir, rsrs.
Eu estava usando o mesmo modelo que você fez passando todos os params como “AsStrings” independente se o field era integer, date ou outros e assim funcionava perfeito, mas somente quando tinha mais de 2 registros pra inserir. Porém quando tinha apenas 1 registro pra inserir, ele me dava o erro de “Arithmetic exception, numeric overflow, or string truncation”, como fosse algum estouro ou passagem errado de tipos de valores, suponho eu. Mas isso só acontecia quando somente estava inserindo somente 1 registro.
Foi quando depois de muito sofrer, mudei pro mesmo tipo do campo, exemplo : “AsLargeInts”, “AsIntegers” e assim por diante.
Estou só repassando, caso alguém tenha esse mesmo problema, e tentar ajudar o próximo rsrs.
De qualquer forma, novamente meus parabéns André, abração! 🙂
Olá, Alessandro, boa tarde!
Muito obrigado pela contribuição! Eu não tinha conhecimento dessa restrição e é importante mencioná-la!
Com certeza a sua experiência irá ajudar outros desenvolvedores.
Obrigado novamente, e abraço! )
André, parabéns pelo tópico.
Tenho uma rotina onde nem sempre sei ao certo quantos registros serão inseridos, existe algumas condicionais.
Và que é preciso determinar o tamanho do array antes de populá-lo.
Sabe se existe alguma implicação eu determinar um size “suficiente” mesmo que eu não vá utilizar todas as posições do array?
Olá, Leonardo! Obrigado pelo feedback.
Ótima pergunta. Não tive a oportunidade de fazer o teste, mas acredito que não há problemas em não utilizar todas as posições do array.
Fiquei curioso com o comando
Execute
, chamado após o preenchimento do array. Este comando também exige a quantidade de inserções como parâmetro, portanto, vale a pena fazer um teste com um tamanho maior do que a quantidade de posições preenchidas. De qualquer forma, eu particularmente acredito que o FireDAC irá “ignorar” as posições vazias.Abraço!
Obs: trabalhei alguns anos com o isolidus de vocês! 🙂
Ótimo material, me ajudou muito no procedimento que preciso. Porém, parei em uma “encruzilhada”.
Arquivo texto: 1.316.604 linhas. Tamanho de cada linha: 171 posições.
Consigo gravar com ArrayDML 25.000 linhas em 1min e 24 seg (sensacional!!).
Fiz um teste que não deu certo. Montei o array com o arquivo texto todo, ou seja, 1.316.604 posições e na hora do execute, eu fiz Execute(25000,0) depois Execute (25000,25001), mas deu “out of memory”… qual seria a melhor forma de gravar os mais de 1 milhão de registros? Agradeço desde já.
Problemas com sql server – “out of memory”. Arquivo TXT com 1 milhão de registros.
Percebi com tentativa e erro que consigo gravar com ArraySize = 25.000, pois bem. Como eu conseguiria trabalhar as linhas restantes? Preciso criar várias listas de 25000 registros por vez? Fiz vários testes, mas todos deram “out of memory”. A única coisa que ainda não testei pq acho que deve ter uma solução melhor, é dividir 1 milhão por 25 mil, e ir adicionando um de cada vez, 25k por 25k… É isso mesmo?
Olá, Leandro, bom dia!
Três fatores podem estar relacionados ao erro:
1) Configuração do banco de dados: não tenho plenos conhecimentos do SQL Server, mas, assim como outros bancos de dados, é possÃvel definir alguns parâmetros relacionados à manipulação de dados em bloco. Por exemplo, se o banco de dados está configurado para aceitar o máximo de 25 mil inserções, e você enviar 50 mil, pode ocorrer o estouro de memória.
2) Leitura do arquivo: atente-se à forma como você está carregando o arquivo TXT. Como são mais de 1 milhão de linhas, talvez seja melhor trabalhar com stream e carregar “partes” do arquivo por vez, realizando inserções parciais. Lembre-se que, quando você carrega um arquivo TXT no Delphi, todo o conteúdo do arquivo fica em memória.
3) O ArrayDML possui suas próprias restrições, que muitas vezes são regidas pelo banco de dados utilizado. Inserir 1 milhão de registros de uma só vez pode ser uma carga muito alta para o banco de dados e realmente exige um espaço considerável na memória. Eu sugiro que você “quebre” a quantidade de inserções em blocos.
Abraço!
André, boa tarde.
Estou usando esta estrutura do DML para fazer a migração dos dados de um banco Oracle para o SQL Server.
Está funcionando com a maioria das tabelas, porém, cheguei em uma que tem 60 parâmetros para passar.
Está dando Out Of Memory.
Não são tantos registros, somente 500 mil.
Fiz, com sucesso, de uma tabela com mais de 3 milhões de registros.
A única diferença é que nesta tabela, que está dando erro, tem mais campos.
O que pode ser?
Desde já, agradeço a atenção e a matéria que me ajudou o começo deste projeto.
Olá, Albani, boa noite!
Que situação curiosa. Se existe um “limite” de uso de memória referente à quantidade de parâmetros, eu desconheço.
Recentemente recebi um e-mail de um desenvolvedor que enfrentou uma situação semelhante. A solução que encontramos, por enquanto, foi “quebrar” as inserções em pequenos blocos de 50 mil registros. Isso pode ser feito no comando
Execute
.Albani, mesmo assim, vale lembrar que algumas restrições são regidas pelo próprio banco de dados de acordo com as configurações definidas. É possÃvel, portanto, que o estouro de memória esteja vindo do banco de dados por não suportar essa quantidade de parâmetros, e não do Delphi. Mas, para ter certeza, terÃamos que avaliar melhor.
Abraço!
André, obrigado pelo conteúdo. Me ajudou.
Estou fazendo a migração dos dados de um banco oracle para um banco SQL.
Estava tudo dando certo até chegar em uma tabela com 60 parâmetros.
Não sei se é por esta quantidade de parâmetros que dá Out of memory, mas, não consegui ver outro fator.
Comecei montando o bloco DML de 5000 registros, cheguei em 100 e, mesmo assim, não funciona.
Já tentei pbByNumber e pbByName.
Já tentei definir o DataType e Size de cada parâmetro.
Nada resolveu.
Você pode me dar mais alguma dica?
Albani, vou entrar em contato!
Boa noite Andre, estou usando esta mesma FDQuery.ParamByName(‘ID’) para 17 campos. Para cada um desse preciso criar parâmetro para eles na Query ou ele entende que os campos são parâmetros?
Bom dia, Judeir!
Você precisa criar os parâmetros manualmente na Query utilizando os dois pontos (“:”), por exemplo:
Abraço!
Boa tarde André consegui, porém preciso incluir vários registros com ID autoincrement, tentei fazer no entanto não consegui tem uma dica
Olá, Judeir.
Ainda não fiz o teste com autoincrement, mas talvez você pode deixar isso a cargo do banco de dados. Com o Firebird, por exemplo, você pode criar Generators para gerar o valor da chave primária a cada inserção na tabela.
Olá André, excelente artigo, me ajudou muito! Porém estou tendo problema com campo Varbinary do SQL. Tentei várias formas mas não funcionou com ArrayDML. Você tem alguma dica?
[Código removido por privacidade]
O erro que ocorre é esse:
Implicit conversion from data type nvarchar to varbinary(max) is not allowed. Use the CONVERT function to run this query.’
Todos dão mesmo erro.
Desde já, Muito obrigado amigo.
Bom dia, Glaubert!
Vou entrar em contato com você.
André boa tarde, primeiramente parabéns pelo conteúdo. Estou tentando fazer a conversão dos dados de um Banco Sybase Anywhere 5.5.04 para o Firebird 2.5 e quero usar ArraySize. Mas estou encontrando problemas com os acentos no banco de origem dos dados (sybase estou usando no bde no langdriver Sybase SQL Dic850.
Pode me dar uma dica de como posso tratar os caracteres especiais?
Obrigado
Olá, Sérgio, tudo bem?
Infelizmente não tenho experiência com Sybase Anywhere. Mesmo assim, este parece ser um problema relacionado ao Unicode. Experimente alterar o charset do seu componente de conexão com o banco de dados de destino (Firebird) para UTF-8. No
TFDConnection
, por exemplo, basta dar um duplo clique, navegar até o parâmetro “CharacterSet” e alterar o seu valor.Abraço!
Boa noite André, como eu passaria um Select de uma Query nesse caso aqui?
lStringListFile.LoadFromFile(‘MOCK.csv’);
Aà vc está usando o arquivo .csv, como eu faço pra usar um Select de um Query no lugar do arquivo MOCK.csv?
Olá, Jhonlemon!
Basta abrir a Query e percorrer os registros. Por exemplo:
A Query na qual você executa o SQL deve ser diferente da Query que fará a inserção, ok?
Abraço!
Boa noite, obrigado pelo retorno…na minha duvida anterior: será que vc pode me ajudar? eu estava querendo dividir o insert do ArrayDML em lote tipo assim de 5mil a 5mil registros, isso é porque esse insert em mobile, então vejo que as vezes o aparelho trava no insert do ArrayDML. Meu código abaixo.
[código removido para manter privacidade]
Olá, Jhonlemon!
Primeiramente, removi o seu código para manter a sua privacidade.
Você pode tirar proveito dos parâmetros do método
Execute
para realizar a inserção em lotes. O primeiro parâmetro é quantidade de inserções que você deseja fazer, e o segundo parâmetro é o deslocamento (a partir de qual registro a inserção deve iniciar). No seu caso, para inserir 5 mil registros por vez, experimente testar dessa forma:Para mais informações, consulte o tópico Command execution da documentação oficial.
Abraço!
André estou usando essa estrutura que vc exemplificou. Só me deparei com um problema. Quando a tabela (destino) tem um campo que é foreign key mesmo sendo permitido null ele retorna erro. Estou usando AsStrings := AsString; Já tentei .values := .value, mas o resultado é o mesmo.
Olá, Sérgio.
Vou entrar em contato com você!
Recurso muito bom, passei por essa necessidade recentemente.
Eu estava utilizando o CopyDataSet (“coStructure”, “coRestart”, “coAppend”), apenas no PC do cliente demorava em torno de 10 minutos para inserir 30k de registros, após realizar a refatoração com o arraydml inserindo em lote de 1k por vez, caiu para 2 segundos.
Acredito que o PC do cliente influencia, memória e processador.
Único detalhe a acrescentar sobre a inserção em lotes, de acordo com a documentação “So, the command will be executed (ATimes – AOffset) times, starting from AOffset row”, eu tive somar o offset no primeiro parâmetro..
// insere os próximos 5 mil registros (a partir do registro 5000)
LQuery.Execute(5000 + 5000, 5000);
Parabéns pelo conteúdo, bem objetivo, me ajudou bastante.
Olá, Marcelo, tudo bem?
Ótima observação! O parâmetro relacionado ao offset realmente confunde alguns programadores. Eu, particularmente, acho que o primeiro parâmetro deveria ser somente a quantidade de vezes, sem fazer cálculos com o offset, mas tudo bem.
De qualquer forma, esse ArrayDML é meio “mágico”, né? 10 minutos para 2 segundos é muita diferença!
Obrigado pelo comentário!
Abraço!
Camarada, fiz conforme seu exemplo porém to tendo um erro na execução index out of bounds quando passo pela segunda linha:
Confesso que não domino o tal do
TStringList
. Esse valor [1] é referente a que? Se eu deixar todos eles como 0 não dá problema, porém, dá outro erro pq aparentemente ele joga um valor de uma coluna em outra, aà como os tamanhos são diferentes, da este erro:345. Data too large for variable [#13]. Max len = [10], actual len = [136] Hint: set the TFDParam.Size to a greater value.
Alguma ideia do que posso fazer pra corrigir isso?
Olá, Eduardo!
Bom, primeiramente, o arquivo utilizado no artigo possui ma estrutura em que os campos são separados por vÃrgulas e os registros separados por linhas, como no exemplo abaixo (ID, Nome e Cidade):
Não tenho certeza se o seu arquivo também segue essa mesma estrutura. Se for diferente, você terá que modificar o código para atender a sua realidade.
Uma instância da classe
TStringList
lê uma lista de strings (texto). Cada posição refere-se à uma parte do texto.No exemplo acima, eu posso usar um objeto da classe
TStringList
para ler cada linha do arquivo, portanto:Em seguida, eu posso usar outra instância de
TStringList
para ler os valores de cada linha. Logo, ao carregar a 1ª linha em um objeto deTStringList
, o resultado será essas posições:Para mais informações sobre a classe
TStringList
, acesse este link.Bom dia!
Obrigado por responder. Mexendo aqui eu descobri que o problema era realmente a forma com que os arquivos são separados, o seu é por virgula e o meu por ponto e virgula. Foi necessário dois ajustes no código pra dar certo:
de:
para:
e acrescentei essa linha:
Assim funcionou perfeitamente. O processo tradicional levava 5:40 (quase 6 minutos), com o ArrayDML caiu para maravilhosos 4 segundos, só alegria.
Muito obrigado por compartilhar essa pérola.
Só tenho uma dúvida, que já foi respondida, mas não ficou claro pra mim, até arrisquei tentar resolver mas não ficou 100%. Quando dé erro durante o processo, uma chave duplicada por exemplo, não consegui fazer com que ele grave a primeira, ou seja, se a chave 2301 existe duas vezes no arquivo, ele não grava nenhuma das duas, os demais registros são salvos normalmente.
Na query que executa o processo eu coloquei esse código:
Pode me dar uma dica nisso?
Olá, Eduardo, que bom que funcionou! O ArrayDML é fantástico 🙂
Sobre o controle de erros, o código que você mencionou “pula” (skip) o registro caso haja um erro de violação de chave primária (ekUKViolated). Neste caso, quando há dois registros com a mesma chave no arquivo, ele deveria gravar somente o primeiro e ignorar o segundo.
No seu cenário, presumo que, ao executar a rotina, o código 2301 já exista no banco de dados, então os dois registros do arquivo acabam sendo ignorados.
Se não é esse o caso, experimente depurar o evento (debug) ou colocar uma mensagem dentro do IF para identificar se os dois registros realmente estão sendo ignorados.
Abraço!