Importação de dados.. .txt para .gdb muito lento.

09/06/2008

3

Olá pessoal,

gostaria da opinião dos colegas do fórum, to precisando importar txt´s para o meu banco de dados .gdb, estou usando os txt´s com campos definidos, tipo ex. abaixo, estou usando importação via mapeamento, tipo, posição inicial, tamanho e tipo de campo, só que o problema que o sistema tá demorando muito pra fazer a importação de arquivos grandes, (só importa de 500 em 500), gostaria de resolver a lentidão do processo de importação. Vou postar a rotina de importação. Estou usando DBExpress, SQLConnection, SqlDataSet, DataSetProvider e ClientDataSet.

// ao abrir o arquivo a ser importado no menu principal do importador..
procedure TfrmPrincipal.btnImportarClick(Sender: TObject);
var
  txt : TStrings;
begin
  if OpenDialog1.Execute then
  begin
    txt := TStringList.Create;
    Try
      txt.LoadFromFile(OpenDialog1.FileName);
        if txt.Count = 0 then
          raise Exception.Create(´Arquivo Vazio´);
          TxtToDataSet(txt,nil,ClientDataSet1,ClientDataSet2);
    Finally
      txt.Free;
    End;

    ShowMessage(´Processamento de Importação concluído com sucesso!!!´);

  end;

end;



// função para o procedimento de importação
procedure TxtToDataSet(Arquivo, Log : TStrings; Destino, Map : TDataSet);
var
  i : integer;
  Campo : String;
begin
  {* Varre o arquivo texto *}
  for I := 1 to Arquivo.Count - 1 do
  begin
    Destino.Append;
    (* para todos os campos a serem mapeados *)
    Try
      Map.First;
      while not Map.Eof do
      begin
        (* Extrai campo conforme especificações de mapeamento *)
        Campo := copy(Arquivo[i],
          Map.FieldByName(´POSI´).AsInteger,
          Map.FieldByName(´TAM´).AsInteger);
        (* Mapeia campo Text para TField *)
        TxtToField(Campo,Destino.FieldByName(Map.FieldByName(´CAMPO´).AsString),
          Map.FieldByName(´TIPO´).AsString[1]);
          Map.Next;
      end;

      destino.Post;

    Except
      on E : Exception do // trata a excessão e não propaga...
      begin
        if Log <> nil then
          Log.Add(Format(´Erro na linha ¬d : ¬s´,[i,E.Message]));
//          Destino.Cancel;
      end;
    End;
  end;
end;


// função para formação dos campos dependendo o tipo de campo.
procedure TxtToField(Text : string; Field : TField; Tipo : Char);
var
  Dia, Mes, Ano : Word;
begin
  case tipo of
    ´M´ : Field.AsCurrency := StrToCurr(Text);
    ´D´ :
    begin
      Dia := StrToInt(copy(Text,1,2));
      Mes := StrToInt(copy(Text,3,2));
      Ano := StrToInt(copy(Text,5,4));
      Field.AsDateTime := EncodeDate(Ano, Mes, Dia);
    end;
    ´A´,´N´ : Field.AsString := Text;
  end;
end;


Se alguém tiver alguma sugestão aí, será de grande valia.
Obrigado


Responder

Posts

09/06/2008

Eniorm

na lista delphi-br rolou um assunto semelhante onde uma pessoa tinha uma grande quantidade de arquivos txt para serem importados pelo banco.

o problema de lentidão estava associado ao fato de que, no caso dele, era aberto uma transação no início, e a mesma só era fechada com commit no final do processamento do arquivo.

neste caso, foi resolvido acrescentando uma variável inteiro com o objetivo de servir de contador, onde era estipulado por exemplo, ao completar 500 inserts, era aplicado um commit, e zerado este contador.

tente implementar algo do tipo, talvez resolve ou pelo menos melhora


Responder

11/09/2008

Cerqueira_r

Bom Dia,

Estou buscando formas de melhorar meu aplicativo de importação de arquivos, atualmente estou utilizando o BDS2006 com banco de dados Postgre 8.2.

Os arquivos que são importados pelo sistema possuem aproximadamente 50Mb, cerca de 300 Mil linhas. Utilizo o componente Zeos com clientdataset.

Tentei modificar o aplicativo para realizar o processo por threads, porém ainda não consegui criar um pool de conexão para sustentar cada thread executada.

Se alguém souber de alguma dica onde possa encontrar referências sobre o assunto, eu gradeço.


Responder

11/09/2008

Eniorm

você tentou fazer algo parecido como explicado no topico acima, de commitar em intervalos de comandos, tipo de 500 a 500?

abs


Responder

11/09/2008

Cerqueira_r

Prezado Enio,

Não encontro uma forma de utilizar transações utilizando o Zeos + Postgre. Segue uma parte do código para que vc possa olhar.

            // inclui o participante
            cdsParticipacao.close();
            cdsParticipacao.CommandText := ´select * from participacao where codcliente=:codcliente and coddevedor=:coddevedor and num_contrato=:num_contrato and cpfcgc=:cpfcgc´;
            cdsParticipacao.Params.ParamByName(´codcliente´).AsString := cdsTP1.FieldByName(´codcliente´).AsString;
            cdsParticipacao.Params.ParamByName(´coddevedor´).AsString := cdsDevAux.FieldByName(´coddevedor´).AsString;
            cdsParticipacao.Params.ParamByName(´num_contrato´).AsString := F.StrZero(cdsTP1.FieldByName(´num_contrato´).AsString,17);
            cdsParticipacao.Params.ParamByName(´cpfcgc´).AsString := F.StrZero(cdsTP1.FieldByName(´cpf_cnpj´).AsString,16);
            cdsParticipacao.Open;

            if cdsParticipacao.Eof then
            begin

              cdsParticipacao.Insert;
              cdsParticipacao.FieldByName(´codcliente´).Value:=cdsTP1.FieldByName(´codcliente´).AsString;
              cdsParticipacao.FieldByName(´coddevedor´).Value:=cdsDevAux.FieldByName(´coddevedor´).AsString;
              cdsParticipacao.FieldByName(´num_contrato´).Value:=F.StrZero(cdsTP1.FieldByName(´num_contrato´).AsString,17);
              cdsParticipacao.FieldByName(´cpfcgc´).Value:=F.StrZero(cdsTP1.FieldByName(´cpf_cnpj´).AsString,16);
              cdsParticipacao.FieldByName(´datcadastro´).Value:=Date();
              cdsParticipacao.FieldByName(´descricao´).Value:=cdsTP1.FieldByName(´descr_partic´).AsString;
              cdsParticipacao.FieldByName(´codparticipacao´).Value:=cdsTP1.FieldByName(´cod_participacao´).AsString;
              cdsParticipacao.Post;
              cdsParticipacao.ApplyUpdates(1);

            end;



Responder

12/09/2008

Diegotiemann

Na empres aonde trabalho desenvolvemos um exportador e importador que IMPORTA 100.000 registros em 6 minutos.

Isso no firebird, mas atenção:
extensão do banco .FDB não GDB;
Sem nenhuma chave extrangeira e primária;
Pra otimizar o desempenho crie um arquivo do banco com um tamanho grande em disco;

Qualquer coisa meu email diegotiemann@gmail.com


Responder

12/09/2008

Eselvati

Colega,


Não trabalho com firebird importando arquivos .txt, mas com mysql importo arquivos com 500.000 (quinhentos mil) registros em [b:0ed5588c98]segundos[/b:0ed5588c98] utilizando o comando
load data infile, verifique se nao existe algo semelhante nele...


Ederson Selvati


Responder

13/09/2008

Adriano Santos

Prezado Enio, Não encontro uma forma de utilizar transações utilizando o Zeos + Postgre. Segue uma parte do código para que vc possa olhar.
procedure TForm1.Button2Click(Sender: TObject);
begin
  // inclui o participante
  with cdsParticipacao.Params do
  begin
    close;
    CommandText := ´select * from participacao where codcliente=:codcliente and coddevedor=:coddevedor and num_contrato=:num_contrato and cpfcgc=:cpfcgc´;
    ParamByName(´codcliente´).AsString := cdsTP1.FieldByName(´codcliente´).AsString;
    ParamByName(´coddevedor´).AsString := cdsDevAux.FieldByName(´coddevedor´).AsString;
    ParamByName(´num_contrato´).AsString := F.StrZero(cdsTP1.FieldByName(´num_contrato´).AsString,17);
    ParamByName(´cpfcgc´).AsString := F.StrZero(cdsTP1.FieldByName(´cpf_cnpj´).AsString,16);
    Open;

    if Eof then
    begin
      Insert;
      FieldByName(´codcliente´).Value:=cdsTP1.FieldByName(´codcliente´).AsString;
      FieldByName(´coddevedor´).Value:=cdsDevAux.FieldByName(´coddevedor´).AsString;
      FieldByName(´num_contrato´).Value:=F.StrZero(cdsTP1.FieldByName(´num_contrato´).AsString,17);
      FieldByName(´cpfcgc´).Value:=F.StrZero(cdsTP1.FieldByName(´cpf_cnpj´).AsString,16);
      FieldByName(´datcadastro´).Value:=Date();
      FieldByName(´descricao´).Value:=cdsTP1.FieldByName(´descr_partic´).AsString;
      FieldByName(´codparticipacao´).Value:=cdsTP1.FieldByName(´cod_participacao´).AsString;
      Post;
      ApplyUpdates(1);
    end;
  end;
end;


Cerqueira, tome cuidado. Seu ApplyUpdates está logo após o Post dentro do Loop? Se estiver, retire-o e coloque depois que o loop terminar. Por que isso? O Post é dado localmente, ou seja, os dados estão sendo gravados no ClientDataSet, na máquina local. Quando executamos um ApplyUpdates, os dados são empacotados e enviados ao servidor. Se estiver fazendo isso a cada Post, sua aplicação ficará lenta, a menos que coloque um contador para que efetue o Apply a cada X registros, exemplo:

......
      Inc(Registros);
      if Registros = 500 then
      begin
        ApplyUpdates(1);
        Regitros := 0;
      end;
.......


[b:d1ed8c84ea]gtts[/b:d1ed8c84ea], mude a rotina e veja se fica mais rápido. Use uma variável do tipo TextFile e importe direto, sem passar por um TStringList. Veja um exemplo
procedure TForm1.Button1Click(Sender: TObject);
var
  Texto : TextFile;
  Campo1 : string;
  Campo2 : string;
  Linha: string;
begin
  AssignFile(Texto, ´C:\Texto.txt´);
  Reset(Texto);

  while not EOF(Texto) do
  begin
    ReadLn(Texto, Linha);
    Campo1 := Copy(Linha, 1, 10);
    Campo2 := Copy(Linha, 11, 10);
    ListBox1.Items.Add(Campo1 + ´-´ + Campo2);
    Next;
  end;
  CloseFile(Texto);
  ShowMessage(´Finalizado´);
end;


Na empres aonde trabalho desenvolvemos um exportador e importador que IMPORTA 100.000 registros em 6 minutos. Isso no firebird, mas atenção: extensão do banco .FDB não GDB; Sem nenhuma chave extrangeira e primária; Pra otimizar o desempenho crie um arquivo do banco com um tamanho grande em disco; Qualquer coisa meu email diegotiemann@gmail.com


Realmente o que o [b:d1ed8c84ea]diegotiemann[/b:d1ed8c84ea] disse tem procedência.


Responder
×
+1 DevUP
Acesso diário, +1 DevUP
Parabéns, você está investindo na sua carreira