我在一单元中写有如下过程:
procedure  file2table(filename: string);
var
  f: textfile;
  ts: TStrings;
  i, r: integer;
  s: string;
begin
  with dmsettle do
  begin
    try
      ADOQuery1.SQL.Clear;
      ADOQuery1.SQL.Add('truncate table usr10sub');
      ADOQuery1.ExecSQL;
    finally
      ADOQuery1.Close;
    end;
    try
      i := 0;
      ADOTable1.TableName := 'usr10sub';
      ADOTable1.Open;
      //ts := TStringList.Create;
      assignfile(f, filename);
      reset(f);
      while not eof(f) do
      begin
        readln(f, s);
        if s = '' then break;
        inc(i);
        subscriber.suiProgressBar1.Position := i;
        //subscriber.suiProgressBar1.Update;
        ts := TStringList.Create;
        extractstrings([chr(01)], [chr(44), chr(39)], pansichar(s), ts);
        try
          //ADOTable1.Append;
          ADOTable1.Insert;
          for r := 0 to (ADOTable1.FieldCount - 1) do
          begin
            case ADOTable1.Fields[r].DataType of
              ftFloat, ftInteger, ftSmallint, ftWord:
              ADOTable1.Fields[r].AsFloat := StrToFloat(ts.Strings[r]);
            else
              ADOTable1.Fields[r].AsString := ts.strings[r];
            end;
          end;
          ADOTable1.Post;
        except
          showmessage(s);
          break;
        end;
        ts.Free;
      end;
    finally
      //ts.Free;
      closefile(f);
      ADOTable1.Close;
    end;
  end;
end;
目的是想把文本文件(大约有30万行,以chr(01)分隔)导入表中,已正常将记录导入到表中,但有一个不明白的地方,程序运行时损耗大量的内存,其中sqlserver也一样损耗大量内存,并且导入速度很慢。各位高手,能说说怎样改进吗?使程序损少量内存!!!

解决方案 »

  1.   

    换思路,先将文本文件预处理为cvs文件,然后用一条SQL语句导入SQLServer
    预处理文件的时候用FileStream或FileMapping,不要一次把所有数据读取进来,读进来一部分处理一部分再写入一部分
      

  2.   

    导致你消耗内存的就是你的ADOTable,它是数据感知控件,实时的,你一条一条加,它就反复感知,而且数据都保存在内存里,建议用ADOQuery 用 Insert语句添加数据.
      

  3.   

    刚开始时,我也考虑了你说的方法,只因表里有太多的字段,写SQL语句时可能会很费劲,而且字段类型有浮点型等!所以考虑用ADOTable组件!不知你有没有更好的办法解决!
      

  4.   

    用ADOCommand提交SQL语句,既然是文件,就无所谓浮点数,只是在SQL语句当中,对字符串增加分界符就好.
      

  5.   

    insert tablename(floatfield,varcharfield,...) values(10.34,'something',...)
    这个SQL文本难道会有问题?
      

  6.   

    给你大概改了一下,你参照着该一下你的程序:procedure  file2table(filename: string); 
    var 
      f: textfile; 
      ts: TStrings; 
      i, r: integer; 
      s: string; 
      List:TStringList;
    begin 
      with dmsettle do 
      begin 
        try 
          ADOQuery1.SQL.Clear; 
          ADOQuery1.SQL.Add('truncate table usr10sub'); 
          ADOQuery1.ExecSQL; 
        finally 
          ADOQuery1.Close; 
        end;     List := TStringList.Create; 
        try 
          assignfile(f, filename); 
          reset(f); 
          List.Clear;
          while not eof(f) do 
          begin 
            readln(f, s); 
            if s = '' then break; 
            subscriber.suiProgressBar1.Position := i; 
            extractstrings([chr(01)], [chr(44), chr(39)], pansichar(s), ts); 
            try 
              List.Add('insert... ');     //此处是添加记录的SQL语句,由你自己填写
            except 
              showmessage(s); 
              break; 
            end;         //此处是每读入1000记录,则一次性添加,你也可以自己调整,每2000条记录一次性添加,但这会增加内存耗用的 
            //注意这种方式,能极大地加快添加数据的速度,此处是关键点
            if List.Count>1000 then   
            begin
              ADOQuery1.Close;
              ADOQuery1.SQL.Clear;
              ADOQuery1.SQL.AddStrings(List);
              ADOQuery1.ExecSQL;
              List.Clear; 
            end;
          end;       if List.Count>0 then   
           begin
            ADOQuery1.Close;
            ADOQuery1.SQL.Clear;
            ADOQuery1.SQL.AddStrings(List);
            ADOQuery1.ExecSQL;
            List.Clear; 
          end;    finally 
          List.Clear;
          FreeAndNil(List);
          closefile(f); 
          ADOTable1.Close; 
        end; 
      end; 
    end; 
      

  7.   

    千万别嫌麻烦,SQL语句一定要写出来,Delphi虽然使用起来方便,但把运行效率降低了,
    所以有些地方要效率,就得自己写。效率和方便是对用户说的,对编程的人来说有时是两者不可兼得的。
      

  8.   

    还有一点,如果SQL语句你是用多行添加到List中的话,你应该弄个计数器,用来记录有多少条记录添加了,然后
    那个 if List.Count>1000 then   应该换成  if 读入的记录数>1000 then
      

  9.   


    MS已经给出了大批量导入/导出的解决方案!
    --摘自MSSQL联机帮助!A. 使用管道从文件导入数据
    此示例将订单详细信息从指定的数据文件导入 AdventureWorks.Sales.SalesOrderDetail 表,并使用竖线 (|) 作为字段终止符,使用 |\n 作为行终止符。BULK INSERT AdventureWorks.Sales.SalesOrderDetail
       FROM 'f:\orders\lineitem.tbl'
       WITH 
          (
             FIELDTERMINATOR =' |',
             ROWTERMINATOR =' |\n'
          )
      

  10.   

    还有最好把SQL语句写成存储过程,在程序这端调用存储过程,这样还能把速度提高
      

  11.   


    这样没有问题。你可能误会了我的意思,我在回复2楼朋友时已经说明了开始时也考虑了用SQL语句的方法,就是因为太繁锁而选用ADOTABLE组件。
      

  12.   


    刚才写漏了:含有chr(63)时...
      

  13.   

    是否会繁琐,看你如何处理,一个Table的所有字段,实际上你可以取出来的,ADOTable本身也是需要去取的.关键看这事情由谁来做.
      

  14.   

    其实你最大的问题就是一次要导入30万笔资料,你可以使用ADOQuery1,            ADOQuery1.Close;
              ADOQuery1.SQL.Clear;
              ADOQuery1.SQL.Text := 'select top 0 * from usr10sub';
              ADOQuery1.Open;然后把你后面代码的ADOTable1换成ADOQuery1, 然后每过1000笔再执行一次以上的代码,这样Local内存和SQL Server的内存应该都会降下来。
      

  15.   

    如果是有格式的文本可以用SELECT INTO直接导进表里
      

  16.   

    1楼方法比较好。利用MSS本身的数据导入功能,让它直接从CVS文件导入数据。由于是MSS自身的功能,效率应该较好。
    难得1楼还比较细心,考虑文本内容较多,分批次来读。
      

  17.   

    ADOQuery的效率远远低于ADOCommand.
      

  18.   

    我知道,只是看楼主的意思似乎是想用 类似 
    “ADOTable1.Fields[r].AsFloat := StrToFloat(ts.Strings[r]); ”
    的代码, 就推荐一下, 总是比ADOTable一直增加数据强, 呵呵。
      

  19.   

    ADOCommand
    ADOQuery
    ADOTable
      

  20.   

    直接提交文本的float数据给SQL,让服务器来转换,不比你在本地转换好得多?
      

  21.   

    我对以上过程作了修改,比较遗憾的是sqlserver进程还是要耗很多的内存,暂时还是没有想出好的办法!过程如下:
    procedure file2table(filename: string);
    var
      f: textfile;
      i, r: integer;
      s, fixsql, varsql: string;
      ts: TStrings;
      ft: array of TFieldType;
    begin
      if not fileexists(filename) then exit;
      with dmsettle do
      begin
        try
          ADOQuery1.SQL.Clear;
          ADOQuery1.SQL.Add('truncate table usr10sub');
          ADOQuery1.ExecSQL;
        finally
          ADOQuery1.Close;
        end;
        //字段列表
        ts := TStringList.Create;
        try
          ADOQuery1.SQL.Clear;
          ADOQuery1.SQL.Add('select * from usr10sub');
          ADOQuery1.Open;
          ADOQuery1.GetFieldNames(ts);
          setlength(ft, (ADOQuery1.FieldCount - 1));
          for i := 0 to (ADOQuery1.FieldCount - 1) do
            ft[i] := ADOQuery1.Fields[i].DataType;
        finally
          ADOQuery1.Close;
        end;
        //组SQL固定部份语句
        fixsql := format('insert into usr10sub(%s', [ts.Strings[0]]);
        for i := 1 to (ts.Count - 1) do
          fixsql := format('%s, %s', [fixsql, ts.Strings[i]]);
        fixsql := format('%s)', [fixsql]);
        i := 0;
        try
          assignfile(f, filename);
          reset(f);
          while not eof(f) do
          begin
            readln(f, s);
            if s = '' then break;
            inc(i);
            subscriber.Label1.Caption := format('正在全量更新 usr10sub 表[%d / %d] ... ', [i, subscriber.suiProgressBar1.Max]);
            subscriber.Label1.Update;
            subscriber.suiProgressBar1.Position := i;
            subscriber.suiProgressBar1.Update;
            ts.Clear;
            extractstrings([chr(01)], [chr(44), chr(39)], pansichar(s), ts);
            case ft[0] of
              ftInteger, ftFloat, ftWord, ftSmallint, ftBCD:
              varsql := format('values(%s', [ts.Strings[0]]);
            else
              varsql := format('values(''%s''', [ts.Strings[0]]);
            end;
            for r := 1 to (ts.Count - 1) do
            begin
              case ft[r] of
                ftInteger, ftFloat, ftWord, ftSmallint, ftBCD:
                varsql := format('%s, %s', [varsql, ts.Strings[r]]);
              else
                varsql := format('%s, ''%s''', [varsql, ts.Strings[r]]);
              end;
            end;
            varsql := format('%s)', [varsql]);
            try
              ADOQuery1.SQL.Clear;
              ADOQuery1.SQL.Add(fixsql);
              ADOQuery1.SQL.Add(varsql);
              ADOQuery1.ExecSQL;
            except
              MessageBox(0, PAnsiChar(format('SUBUNI: %s [%d]', [ADOConnection1.Errors[0].Description, ADOConnection1.Errors[0].NativeError])), PAnsiChar(subscriber.Caption), MB_OK + MB_ICONERROR);
              break;
            end;
          end;
        finally
          ADOQuery1.Close;
          closefile(f);
        end;
      end;
      ts.Free;
    end;
    很感谢1楼、2楼、... 9楼、10楼等的朋友,是他(她)们给了我很多的帮助,谢谢你们!