将记录从一个 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 获取记录
将 Django DB 从 SQLite 迁移到 MySQL 的最佳方法是啥?