将记录从一个 db 迁移到 mysql 使用 Delphi 和 Firedac 非常慢

Posted

技术标签:

【中文标题】将记录从一个 db 迁移到 mysql 使用 Delphi 和 Firedac 非常慢【英文标题】:Migrating records from one db to mysql it is very slow with Delphi and Firedac 【发布时间】:2017-10-11 19:53:21 【问题描述】:

我必须将多条超过 100000 条的记录从 Advantage Database Local 迁移到 mysql(本地)。

我正在使用此代码进行测试,但结果非常缓慢。超过2小时 迁移 120000 条记录。

我需要一些帮助来提高速度,可能需要修改代码或一些 fireac 属性。

代码很简单。我从 Advantable 表中请求所有记录,并对每条记录执行循环并插入到 mysql 本地数据库中。

    // ADVANTAGE TABLE
    DM.Qry_Usb.SQL.Clear;
    DM.Qry_USB.SQL.Add( 'Select * from Cabezal' );
    DM.Qry_USB.SQL.Add( 'order by Fecha, Pedido' );
    DM.Qry_USB.Open;

    while not DM.Qry_USB.Eof do
    begin

        DM.FDQuery.SQL.Clear;    // MYSQL DB
        DM.FDQuery.SQL.Add( 'Insert Into Cabezal ' );
        DM.FDQuery.SQL.Add( '( Correla, Fecha, Hora, Cliente, Pedido, Direccion, ');
        DM.FDQuery.SQL.Add( 'Entre, yEntre, Estado, Total, PedidoxWeb ) ' );
        DM.FDQuery.SQL.Add( 'Values ( ' );
        DM.FDQuery.SQL.Add( IntToStr( DM.Qry_USB.FieldByName('Correla').AsInteger ) + ',' );
        DM.FDQuery.SQL.Add( QuotedStr( FormatDateTime( 'DD.MM.YYYY', DM.Qry_Usb.FieldByName('Fecha').AsDateTime ) ) + ',' );
        DM.FDQuery.SQL.Add( QuotedStr( FormatDateTime( 'HH:MM:SS', DM.Qry_Usb.FieldByName('Hora').AsDateTime ) ) + ',' );
        DM.FDQuery.SQL.Add( IntToStr( DM.Qry_USB.FieldByName('Cliente').AsInteger ) + ',' );
        DM.FDQuery.SQL.Add( IntToStr( DM.Qry_USB.FieldByName('Pedido').AsInteger ) + ',' );
        DM.FDQuery.SQL.Add( QuotedStr( DM.Qry_USB.FieldByName('Direccion').AsString ) + ',' );
        DM.FDQuery.SQL.Add( QuotedStr( DM.Qry_USB.FieldByName('Entre').AsString ) + ',' );
        DM.FDQuery.SQL.Add( QuotedStr( DM.Qry_USB.FieldByName('YEntre').AsString ) + ')' );
        DM.FDQuery.ExecSQL;

        DM.Qry_USB.Next;

    end;
    DM.FDConnection.Commit;

我正在修改代码以使用参数,但仍然无法测试。

我还能做些什么来让这段代码更快??

非常感谢。

最好的祝福。

【问题讨论】:

使用TFDBatchMove。您的代码至少有这两个缺陷,通过游标迭代数据集和重复的命令准备(您可以迭代底层源数据存储并为目标准备一次命令并仅更改参数)。尽管如此,TFDBatchMove 只是用于此目的的组件。 @Victoria 所说的。如果您出于某种原因必须使用循环来执行此操作。不要为 Qry_USB 中的每条记录执行所有这些 FieldByName 调用 - 在循环开始之前查找它们并将它们分配给本地字段变量。每隔几十行提交一次目标事务。 并停止清除行间的 SQL。使用参数,在循环之前设置一次 SQL,然后只需设置参数值并在循环内执行 SQL。这让服务器可以通过设置(识别参数,为它们创建占位符)和编译 SQL 来优化它,然后缓存该编译语句。说 我正在修改以使用参数但仍然无法测试它 没有意义。你按照我的指示修改它,然后执行代码,完成后你就完成了。 好的,谢谢大家的回答。我会试试你提到的,然后告诉你。 没什么好分析的。忘记你的代码,忘记我们所说的,学习如何使用TFDBatchMove。它将在内部使用 DML 数组技术,这应该是将数据从一个 DBMS 移动到另一个 DBMS 的最快方式。它也足够灵活,可以满足您的任务。 @Ken,DML 数组插入也可以大大改善这项任务(对于 MySQL,它是本机的)。在这种情况下,这将是重新发明一个***。 【参考方案1】:

解决类似问题的示例。

http://docwiki.embarcadero.com/CodeExamples/Sydney/en/FireDAC.TFDQuery.Batch_Sample

  FDQuery1.SQL.add('sELECT * from cars');
  FDQuery1.open;


  FmMain.FDConnection1.StartTransaction;
  FDQuery2.sql.clear;
  FDQuery2.sql.add('INSERT INTO tributosasociados (id,NroContrib, CodTributo, CodImponible,descripcion,BAseImponible,Alicuota,FechaAlta,activo )');
  FDQuery2.sql.Add('VALUES(null, :NroContrib,:CodTributo,:CodImponible, :descripcion, :BAseImponible,:Alicuota,:FechaAlta,:activo)');


  FDQuery2.Params.ArraySize := FDQuery1.RecordCount;
   i := 0;
  while not FDQuery1.eof do
   begin
     FDQuery2.ParamByName('CodTributo').AsIntegers[i] := 3;
     FDQuery2.ParamByName('NroContrib').AsIntegers[i] := FDQuery1.FieldByName('NroContrib').AsInteger;
     FDQuery2.ParamByName('CodImponible').AsStrings[i] := FDQuery1.FieldByName('dominio').AsString;
     FDQuery2.ParamByName('descripcion').AsStrings[i] := FDQuery1.FieldByName('descripcion').AsString +' / '+ FDQuery1.FieldByName('anio').AsString;
     FDQuery2.ParamByName('BAseImponible').AsFloats[i] := FDQuery1.FieldByName('valuacion').AsFloat;
     FDQuery2.ParamByName('Alicuota').AsFloats[i] := 1.7; // FDQuery1.FieldByName('****Ver Alicuta BUTA****').AsFloat;
     FDQuery2.ParamByName('FechaAlta').AsdateTimes[i] := FDQuery1.FieldByName('FechaAlta').AsdateTime;
     FDQuery2.ParamByName('activo').AsIntegers[i] := 1;

     FDQuery1.next;
     i := i+1;
   end;

  FDQuery2.Execute(FDQuery2.Params.ArraySize);

  FmMain.FDConnection1.Commit;

 FDQuery1.close;
 FDQuery2.close;
 ShowMessage( intToStr(i) );

【讨论】:

您能否澄清您的答案以及这如何解决所提出的问题?

以上是关于将记录从一个 db 迁移到 mysql 使用 Delphi 和 Firedac 非常慢的主要内容,如果未能解决你的问题,请参考以下文章

将本地 mongo db 迁移到 atlas 后无法从 atlas 获取记录

MySQL将失败记录插入另一个表

将 Django DB 从 SQLite 迁移到 MySQL 的最佳方法是啥?

使用Ruby从MySQL迁移到DB2

将 Hypersonic DB 迁移到 MySQL/MariaDB

docker - 如何将 mysql 数据从本地迁移到 ec2 实例