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分钟。