将多个CSV文件导入到数据集中的Delphi速度降低

时间:2019-03-16 09:58:43

标签: csv delphi

我正在使用Delphi 7,Windows 7和绝对数据库。

快速背景。我在一家慈善商店工作,该商店依赖捐赠给我们的物品。要从我们的HMRC中收回“礼品援助”,我们必须提交所有销售的详细信息以及该销售的每个捐赠者的姓名和地址。我们为有特殊需要的人提供帮助,因此准确的数据输入非常重要。

到目前为止,检查邮递区号的验证很容易(基于我们的本地区域),基本上是AA00_0AAAA0_0AA的格式。随着我们的名字越来越知名,并不是所有的邮政编码都遵循这些规则。

我可以访问英国的皇家邮件数据库,以获取在英国的地址,因为我想将输入的邮政编码与真实的邮政编码进行实际比较。 RM csv文件很大,因此我使用GSplit3将其分解为更易于管理的文件。这给了我492个csv文件,每个文件包含大约62000行。请注意,我只对邮政编码感兴趣,因此存在大量重复。

要将这些文件加载​​到数据集中(不重复),我首先将文件名加载到列表框中,然后运行一个循环以遍历所有文件以复制到数据服务器。为避免重复,我尝试在文件名上放置唯一索引字段,但是即使在Delphi之外运行,我仍然收到有关重复的错误消息。然后,我尝试捕获要添加的最后一条记录的文本,然后将其与下一条记录进行比较

   procedure TForm1.importClick(Sender: TObject);
    var
     i,y:Integer;
     lstfile:string;
    begin
      for i:= 0 to ListBox1.Items.Count-1 do
        begin
          lstfile:='';
          cd.Active:=False;//cd is a csv dataset loaded with csv file
          cd.FileName:='C:\paf 112018\CSV PAF\'+ListBox1.Items[i];
          cd.Active:=True;
          while not cd.Eof do
            begin
              if (lstfile:=cd.Fields[0].AsString=cd.Fields[0].AsString) then cd.Next 
            else
              table1.append;
               table1.fields[0].asstring:=cd.Fields[0].AsString;
               lstfile:=cd.Fields[0].AsString;
               cd.Next;
            end;
         end;
   table1.Edit;
   table1.Post;
   end;

尽管数据集中的记录总数似乎很少,但这似乎可以正常工作。我检查了自己的邮编,尽管找到了另一个邮编,但它不存在。因此显然记录已被跳过。然后,我尝试使用dupignore将CSV文件加载到字符串列表中,然后将字符串列表复制到数据集中。

        unit Unit1;

        interface

        uses
          Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
          Dialogs, Grids, DBGrids, SMDBGrid, StdCtrls, DB, ABSMain, SdfData;

        type
          TForm1 = class(TForm)
            cd: TSdfDataSet;
            dscd: TDataSource;
            dst: TDataSource;
            ABSDatabase1: TABSDatabase;
            table1: TABSTable;
            table1PostCode: TStringField;
            Label2: TLabel;
            ListBox1: TListBox;
            getfiles: TButton;
            import: TButton;
            procedure getfilesClick(Sender: TObject);
            procedure importClick(Sender: TObject);

          private
            { Private declarations }
          public
            { Public declarations }
          end;

        var
          Form1: TForm1;
          num:Integer;
        implementation

        {$R *.dfm}
        procedure ListFileDir(Path: string; FileList: TStrings);
        var
          SR: TSearchRec;
        begin
          if FindFirst(Path + '*.csv', faAnyFile, SR) = 0 then
             begin
              repeat
              if (SR.Attr <> faDirectory) then
                begin
                  FileList.Add(SR.Name);
                end;
              until FindNext(SR) <> 0;
              FindClose(SR);
             end;
        end;

        procedure TForm1.getfilesClick(Sender: TObject);
        begin//Fill listbox with csv files
         ListFileDir('C:\paf 112018\CSV PAF\', ListBox1.Items);
        end;

          //start to iterate through files to appane to dataset
        procedure TForm1.importClick(Sender: TObject);
        var
          i,y:Integer;
          myl:TStringList;
        begin

         for i:= 0 to ListBox1.Items.Count-1 do
          begin
            myl:=TStringList.Create;
            myl.Sorted:=True;
            myl.Duplicates:=dupIgnore;
            cd.Active:=False;
            cd.FileName:='C:\paf 112018\CSV PAF\'+ListBox1.Items[i];
            cd.Active:=True;
            while not cd.Eof do
              begin
               if (cd.Fields[0].AsString='')then cd.Next 
              else
                myl.Add(cd.Fields[0].AsString);
                cd.Next;
              end;
            for y:= 0 to myl.Count-1 do
              begin
               table1.Append;
               table1.Fields[0].AsString:=myl.Strings[y];
              end;
          myl.Destroy;
          end;
          t.Edit;
          t.Post;
        end;


        procedure TForm1.Button1Click(Sender: TObject);
        begin
        t.Locate('Post Code',edit1.text,[]);
        end;

        procedure TForm1.Button2Click(Sender: TObject);
        var
          sel:string;
        begin
        q.Close;
        q.SQL.Clear;
        q.SQL.Add('Select * from postc where [Post Code] like :sel');
        q.ParamByName('sel').AsString:=Edit1.Text;
        q.Active:=True;
        end;

        end.

这很好启动,但由于内存泄漏,我很快开始变慢,我尝试过myl.free(),freeandnil(myl)并最终销毁,但是它们都很快变慢。我不是专家,但是喜欢使用Delphi,并且通常会设法通过您的页面或Google搜索来解决问题,但是这次我很困惑。任何人都可以建议一种更好的方法

1 个答案:

答案 0 :(得分:2)

下面显示了如何从包含邮政编码列表的文件中添加邮政编码 到查询类型的数据集,例如TAdoQuery或您的q数据集(您的q并没有说明什么类型 据我所知)。

从您所说的内容来看,尽管您对待邮政编码文件也似乎 作为CVS文件,您实际上不需要:如果记录仅包含一个 字段,不需要逗号,因为没有可分开的内容, 该文件每行应仅包含一个邮政编码。因此,那里 似乎并不需要增加将其作为CSV文件加载的开销,因此您应该能够简单地将其加载到TStringList并从那里添加邮政编码。

我不会尝试更正您的代码,仅显示一个非常简单的示例说明我认为应该如何完成。 因此,以下代码将打开一个邮政编码列表文件(假定每行包含一个邮政编码),请检查其中是否每个条目 已存在于您的邮政编码表中,如果没有,则将其添加。

  procedure TForm1.AddPostCodes(const PostCodeFileName: String);
  //  The following shows how to add postcodes to a table of existing ones
  //  from a file named PostCodeFileName which should include an explicit path
  var
    PostCodeList : TStringList;
    PostCode : String;
    i : Integer;
  begin
    PostCodeList := TStringList.Create;
    try
      PostCodeList.LoadFromFile(PostCodeFileName);
      if qPostCodes.Active then
        qPostCodes.Close;
      qPostCodes.Sql.Text := 'select * from postcodes order by postcode';

      qPostCodes.Open;
      try
        qPostCodes.DisableControls;  //  Always call DisableControls + EnableControls
        //  when iterating a dataset which has db-aware controls connected to it
        for i := 0 to PostCodeList.Count - 1 do begin
          PostCode := PostCodeList[i]; //  use of PostCode local variable is to assist debuggging
          if not qPostCodes.Locate('PostCode', PostCode, [loCaseInsensitive]) then
            qPostCodes.InsertRecord([PostCode]);  //  InsertRecord does not need to be foollowed by a call to Post.
        end;
      finally
        qPostCodes.EnableControls;
        qPostCodes.Close;
      end;
    finally
      PostCodeList.Free;  //  Don't use .Destroy!
    end;
  end;

顺便说一句,关于括号内的数据集迭代的注释 通过调用DisableControls和EnableControls,通常这样做的原因是避免过度更新连接到数据集的任何db-aware控件的gui显示。但是,我之所以这么做的原因之一 不愿意推测导致您变慢的原因是TAdoQuery, 这是用Delphi编码的标准数据集类型之一 从DisableControls / EnableControls中获取,即使没有db感知控件 连接到它。这是由于TAdoQuery编码中的一个古怪之处。随你 您正在使用的数据集可能会有类似的怪癖。