FireDac三种方式批量添加数据的性能对比

Posted Luo大哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FireDac三种方式批量添加数据的性能对比相关的知识,希望对你有一定的参考价值。

我有个程序,需要从CSV中读入数据,对数据进行分析后,然后插入另一个sqlite数据库的数据表。在我的程序中使用了virtual string tree和Firedac,数据大约有13000条,需要转存的数据有11000条左右,转存的字段有8条,除了一条是boolean类型的外都是string类型。

1、直接插入记录

我刚开始采用的方式是对VST的每一条记录进行处理,以前在办公室的机器上,速度虽然有点慢,但还可以接受,大概需要2分多钟,代码如下:

var s:string;  T1:Cardinal;
begin
  s:=InputBox(\'请输入转存的AIRAC周期号\',\'AIRAC周期\',\'\');
  T1:=GetTickCount;
  if s.Trim.IsEmpty  then Exit;
  Form2.g1.MaxValue:=vst1.RootNodeCount;
  Form2.g1.Progress:=0;
  Form2.Label1.Caption:=\'数据转换需要较长时间,请耐心等候……\';
  Form2.Show;
Form2.Update;
  vst1.IterateSubtree(nil,exporttotable,PChar(s)) ;
  
  con2.Close;
  Form2.Close;
  ShowMessage(\'耗时:\'+(GETTICKCOUNT-T1).ToString()+\'毫秒\');
end;

而VST遍历函数调用的procedure exporttotable 代码如下:

VAR RMKSL:STRING;s:string; I:Integer;
begin
  if Form2.Showing then Form2.g1.Progress:=Form2.g1.Progress+1;
  s:=string(data);
  with pflight(vst1.GetNodeData(node))^ do
  begin
    if (dep_code.Length<>4) or (arr_code.Length<>4) then  Exit;
    RMKSL:=\'T_DIS=\'+t_dis+#13#10+
           \'MIN_ALT=\'+min_alt+#13#10+
           rmk;
    SD1.ExecSQL(\'insert into al_fpl(CODE,dep_apt,arr_apt,fpl,"limit",rmk,airac,is_new) \'+
                 \'values(:code,:dep,:arr,:fpl,:limit,:rmk,:AR,:isnew)\',
                [flt_id,dep_code,arr_code,fpl,limit, RMKSL,s,isnew] );
  end;
end;

在另一台旧电脑上运行,需要大约420多秒,这就有点让人难以接受了。不过好在有进度条显示,不至于造成死机的错觉。

2、使用DML插入数据

后来我换成使用DML插入数据,代码变成了:

var s:string;  T1:Cardinal;
begin
  s:=InputBox(\'请输入转存的AIRAC周期号\',\'AIRAC周期\',\'\');
  T1:=GetTickCount;
  if s.Trim.IsEmpty  then Exit;
  Form2.g1.MaxValue:=vst1.RootNodeCount;
  Form2.g1.Progress:=0;
  Form2.Label1.Caption:=\'数据转换需要较长时间,请耐心等候……\';
  Form2.Show;
  Form2.Update;
  con2.ExecSQL(\'DELETE FROM AL_FPL WHERE AIRAC=:AR\',[s]);
  SD1.SQL.Text:=\'insert into al_fpl(CODE,dep_apt,arr_apt,fpl,"limit",rmk,airac,is_new) \'+
                 \'values(:code,:dep,:arr,:fpl,:limit,:rmk,:AR,:isnew)\';
  SD1.Params.ArraySize:=vst1.RootNodeCount;
  
  vst1.IterateSubtree(nil,exporttotable,PChar(s)) ;

  SD1.Execute(vst1.RootNodeCount);
  con2.Close;
  Form2.Close;
  ShowMessage(\'耗时:\'+(GETTICKCOUNT-T1).ToString()+\'毫秒\');
end;

遍历函数的代码是:

VAR RMKSL:STRING;s:string; I:Integer;
begin
  if Form2.Showing then Form2.g1.Progress:=Form2.g1.Progress+1;
  s:=string(data);
  with pflight(vst1.GetNodeData(node))^ do
  begin
    if (dep_code.Length<>4) or (arr_code.Length<>4) then  Exit;
    RMKSL:=\'T_DIS=\'+t_dis+#13#10+
           \'MIN_ALT=\'+min_alt+#13#10+
           rmk;
    i:=node.index;
    SD1.Params[0].AsStrings[I]:=flt_id;
    SD1.Params[1].AsStrings[I]:=dep_code;
    SD1.Params[2].AsStrings[I]:=arr_code;
    SD1.Params[3].AsStrings[I]:=fpl;
    SD1.Params[4].AsStrings[I]:=limit;
    SD1.Params[5].AsStrings[I]:=RMKSL;
    SD1.Params[6].AsStrings[I]:=S;
    SD1.Params[7].AsStrings[I]:=flt_id;
  end;
end;

运行结果更慢,大约要460秒,而且最后转换记录部分没有提示条,好像死机了一般。

3、使用FDMEMTABLE和FDLocalSQL

这种方式是先把数据存入到FDMEMTABLE中,然后属地化FDM到同一个库中,最后执行SQL语句insert into table插入数据,代码如下:

var s:string;  T1:Cardinal;
begin
  s:=InputBox(\'请输入转存的AIRAC周期号\',\'AIRAC周期\',\'\');
  T1:=GetTickCount;
  if s.Trim.IsEmpty  then Exit;
  Form2.g1.MaxValue:=vst1.RootNodeCount;
  Form2.g1.Progress:=0;
  with FDM do
  begin
    with FieldDefs.AddFieldDef do
    begin
      Name:=\'CODE\';
      DataType:=ftString;
      Size:=10;
    end;
    with FieldDefs.AddFieldDef do
    begin
      Name:=\'DEP_APT\';
      DataType:=ftString;
      Size:=10;
    end;
    with FieldDefs.AddFieldDef do
    begin
      Name:=\'ARR_APT\';
      DataType:=ftString;
      Size:=10;
    end;
    with FieldDefs.AddFieldDef do
    begin
      Name:=\'FPL\';
      DataType:=ftString;
      Size:=500;
    end;
    with FieldDefs.AddFieldDef do
    begin
      Name:=\'IS_NEW\';
      DataType:=ftBoolean;
    end;
    with FieldDefs.AddFieldDef do
    begin
      Name:=\'LIMIT\';
      DataType:=ftString;
      Size:=500;
    end;
    with FieldDefs.AddFieldDef do
    begin
      Name:=\'RMK\';
      DataType:=ftString;
      Size:=500;
    end;
    with FieldDefs.AddFieldDef do
    begin
      Name:=\'AIRAC\';
      DataType:=ftString;
    end;
    FDM.Open;
  end;
  Form2.Label1.Caption:=\'数据转换需要较长时间,请耐心等候……\';
  Form2.Show;
  Form2.Update;
  con2.ExecSQL(\'DELETE FROM AL_FPL WHERE AIRAC=:AR\',[s]);
  vst1.IterateSubtree(nil,exporttotable,PChar(s)) ;
  FDM.LocalSQL:=FDLocalSQL1;
  SD1.ExecSQL(\'insert into al_fpl(CODE,dep_apt,arr_apt,fpl,is_new,"limit",rmk,airac) \'+
              \'SELECT * FROM FDM\');
  con2.Close;
  Form2.Close;
  FDM.LocalSQL:=nil;
  fdm.ClearDetails;
  FDM.Close;
  ShowMessage(\'耗时:\'+(GETTICKCOUNT-T1).ToString()+\'毫秒\');
end;

而遍历函数调用的程序代码如下:

VAR RMKSL:STRING;s:string; I:Integer;
begin
  if Form2.Showing then Form2.g1.Progress:=Form2.g1.Progress+1;
  s:=string(data);
  with pflight(vst1.GetNodeData(node))^ do
  begin
    if (dep_code.Length<>4) or (arr_code.Length<>4) then  Exit;
    RMKSL:=\'T_DIS=\'+t_dis+#13#10+
           \'MIN_ALT=\'+min_alt+#13#10+
           rmk;
    I:=node.Index;
    FDM.Append;
    FDM.Fields[0].AsString:=flt_id;
    FDM.Fields[1].AsString:=dep_code;
    FDM.Fields[2].AsString:=arr_code;
    FDM.Fields[3].AsString:=fpl;
    FDM.Fields[4].AsBoolean:=ISNEW;
    FDM.Fields[5].AsString:=LIMIT;
    FDM.Fields[6].AsString:=RMKSL;
    FDM.Fields[7].AsString:=S;
    FDM.Post;
  end;
end;

代码部分长一些,但结果令人满意:

 

短的时候700毫秒,快的时候也不超过1分钟。

以上是关于FireDac三种方式批量添加数据的性能对比的主要内容,如果未能解决你的问题,请参考以下文章

firedac二进制序列和JSON序列的对比

MySQL批量插入数据的四种方案(性能测试对比)

count,count(*),count(主键) 性能对比

FireDAC 下的批量 SQL 命令执行

FireDAC 下的批量 SQL 命令执行

Golang 中三种读取文件发放性能对比