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

发布时间 2023-06-17 23:53:38作者: Luo大哥

我有个程序,需要从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分钟。