Delphi - 多次插入到MS SQL 2014中

时间:2015-03-13 23:50:04

标签: sql-server delphi insert

我需要循环抛出tstringlist并为每个项执行insert。现在我的代码是:

 for i := 0 to TS2.Count - 1 do
      begin

        aqZapisz.SQL.Text := 'INSERT INTO projekty_koszty_rozb ' +
          '(id_kosztu,id_projektu, nr_dokumentu, pozycja,id_grupy, id_rodzaju, id_typu, data_dok, '
          + 'data_pla, data_ksi,mc,rok,kwota) ' +
          'VALUES(:p1,:p2, :p3, :p4, :p5, :p6, :p7, :p8, :p9,:p10,:p11,:p12,:p13)';
        aqZapisz.Parameters.ParamByName('p1').Value := idk;
        aqZapisz.Parameters.ParamByName('p2').Value := TS2[i];
        aqZapisz.Parameters.ParamByName('p3').Value := edtNrDok.Text;
        aqZapisz.Parameters.ParamByName('p4').Value := edtPoz.Text;
        aqZapisz.Parameters.ParamByName('p5').Value := id_grupy;;

        aqZapisz.Parameters.ParamByName('p6').Value := id_rodzaju;
        aqZapisz.Parameters.ParamByName('p7').Value := id_typu;
        aqZapisz.Parameters.ParamByName('p8').Value :=
          DateToStr(zxDateDok.Date);
        aqZapisz.Parameters.ParamByName('p9').Value :=
          DateToStr(zxDatePlat.Date);
        aqZapisz.Parameters.ParamByName('p10').Value :=
          DateToStr(zxDateKsieg.Date);
        aqZapisz.Parameters.ParamByName('p11').Value :=
          IntToStr(Integer(MonthOf(zxDateKsieg.Date)));
        aqZapisz.Parameters.ParamByName('p12').Value :=
          IntToStr(Integer(YearOf(zxDateKsieg.Date)));
          aqZapisz.Parameters.ParamByName('p13').Value :=
            RoundTo((StrToFloat(edtWartosc.Text) /
            listazaznprojektow.Count), -2);

        aqZapisz.ExecSQL;
        aqZapisz.SQL.Clear;

      end;

但插入时间约为2分钟,TS2中有1700个项目。我怎样才能提高速度?

4 个答案:

答案 0 :(得分:1)

每次循环都不要替换你的SQL语句。在循环开始之前设置一次,然后只更新循环内的参数。

aqZapisz.SQL.Text := 'INSERT INTO projekty_koszty_rozb ' +
      '(id_kosztu,id_projektu, nr_dokumentu, pozycja,id_grupy, id_rodzaju, id_typu, data_dok, '
      + 'data_pla, data_ksi,mc,rok,kwota) ' +
      'VALUES(:p1,:p2, :p3, :p4, :p5, :p6, :p7, :p8, :p9,:p10,:p11,:p12,:p13)';

for i := 0 to TS2.Count - 1 do
begin
  aqZapisz.Parameters.ParamByName('p1').Value := idk;
  aqZapisz.Parameters.ParamByName('p2').Value := TS2[i];
  aqZapisz.Parameters.ParamByName('p3').Value := edtNrDok.Text;
  aqZapisz.Parameters.ParamByName('p4').Value := edtPoz.Text;
  aqZapisz.Parameters.ParamByName('p5').Value := id_grupy;;
  aqZapisz.Parameters.ParamByName('p6').Value := id_rodzaju;
  aqZapisz.Parameters.ParamByName('p7').Value := id_typu;
  aqZapisz.Parameters.ParamByName('p8').Value :=  
    DateToStr(zxDateDok.Date);
  aqZapisz.Parameters.ParamByName('p9').Value := 
    DateToStr(zxDatePlat.Date);
  aqZapisz.Parameters.ParamByName('p10').Value := 
    DateToStr(zxDateKsieg.Date);
  aqZapisz.Parameters.ParamByName('p11').Value :=
      IntToStr(Integer(MonthOf(zxDateKsieg.Date)));
  aqZapisz.Parameters.ParamByName('p12').Value :=
      IntToStr(Integer(YearOf(zxDateKsieg.Date)));
  aqZapisz.Parameters.ParamByName('p13').Value :=
      RoundTo((StrToFloat(edtWartosc.Text) /
        listazaznprojektow.Count), -2);

  aqZapisz.ExecSQL;
end;

答案 1 :(得分:1)

您可以使用

提高速度
  1. 单笔交易

    Connection.StartTransaction;
    try
    
      // Send the data to the server
    
      Connection.Commit;
    except
      ConnectionRollback;
    end;
    
  2. 将数据拆分为固定和动态部分,因此您不必一次又一次地向服务器发送相同的数据。将固定部分存储到临时表中,并将其用于包含动态数据的每个插入

  3. 重复使用已准备好的查询 - >不要在循环中设置sql语句或清除它以便再次设置
  4. 重复使用已设置的固定参数(替代2。)
  5. 快速解决方案

    aqZapisz.Connection.StartTransaction;
    try
      aqZapisz.SQL.Text :=
        'INSERT INTO projekty_koszty_rozb ' +
        '(id_kosztu,id_projektu, nr_dokumentu, pozycja,id_grupy, id_rodzaju, id_typu, data_dok, ' +
        'data_pla, data_ksi,mc,rok,kwota) ' +
        'VALUES(:p1,:p2, :p3, :p4, :p5, :p6, :p7, :p8, :p9,:p10,:p11,:p12,:p13)';
    
      aqZapisz.Parameters.ParamByName( 'p1' ).Value := idk;
      aqZapisz.Parameters.ParamByName( 'p3' ).Value := edtNrDok.Text;
      aqZapisz.Parameters.ParamByName( 'p4' ).Value := edtPoz.Text;
      aqZapisz.Parameters.ParamByName( 'p5' ).Value := id_grupy;;
      aqZapisz.Parameters.ParamByName( 'p6' ).Value := id_rodzaju;
      aqZapisz.Parameters.ParamByName( 'p7' ).Value := id_typu;
      aqZapisz.Parameters.ParamByName( 'p8' ).Value := DateToStr( zxDateDok.Date );
      aqZapisz.Parameters.ParamByName( 'p9' ).Value := DateToStr( zxDatePlat.Date );
      aqZapisz.Parameters.ParamByName( 'p10' ).Value := DateToStr( zxDateKsieg.Date );
      aqZapisz.Parameters.ParamByName( 'p11' ).Value := IntToStr( Integer( MonthOf( zxDateKsieg.Date ) ) );
      aqZapisz.Parameters.ParamByName( 'p12' ).Value := IntToStr( Integer( YearOf( zxDateKsieg.Date ) ) );
      aqZapisz.Parameters.ParamByName( 'p13' ).Value := RoundTo( ( StrToFloat( edtWartosc.Text ) / listazaznprojektow.Count ), -2 );
    
      for i := 0 to TS2.Count - 1 do
        begin
          aqZapisz.Parameters.ParamByName( 'p2' ).Value := TS2[i];
    
          aqZapisz.ExecSQL;
        end;
    
      aqZapisz.Connection.Commit;
    except
      aqZapisz.Connection.Rollback;
      raise;
    end;
    

    如果值得实施,您应该测量应用程序和服务器任务获取提示所需的时间(2.)。

    对于这种情况,较新的Delphi版本有一个方便的System.Diagnostics.TStopWatch

    var
      LInternal, LServer : TStopWatch;
    begin
      LServer := TStopWatch.Create;
      LInternal := TStopWatch.StartNew;
    
      ...
    
        for i := 0 to TS2.Count - 1 do
          begin
            aqZapisz.Parameters.ParamByName( 'p2' ).Value := TS2[i];
    
            LServer.Start;
    
            aqZapisz.ExecSQL;
    
            LServer.Stop;
          end;
    
      ...
    
      LInternal.Stop;
    
      ShowMessage( 
        Format( 
          'Total: %dms Server: %dms',
          [ LInternal.ElapsedMilliseconds, LServer.ElapsedMilliseconds ] ) );
    
    end;
    

答案 2 :(得分:0)

最大的打击是你一个接一个地做多个SQL语句。这包括调用服务器,等待响应以及随之而来的所有额外流量。你有几个选择:

1)您可以尝试依次添加所有sql语句,然后在最后执行一个execsql语句。您将需要“作弊”并内联p2参数。如果您选择此路由,则应自行清理该参数以防止SQL注入等问题。您也可以批量生成X记录。

baseSQL1 := 'INSERT INTO projekty_koszty_rozb ' +
      '(id_kosztu,id_projektu, nr_dokumentu, pozycja,id_grupy, id_rodzaju, id_typu, data_dok, '
      + 'data_pla, data_ksi,mc,rok,kwota) ' +
      'VALUES(:p1, '+
baseSQL2 := ', :p3, :p4, :p5, :p6, :p7, :p8, :p9,:p10,:p11,:p12,:p13);';

aqZapisz.SQL.Clear;
for i := 0 to TS2.Count - 1 do
begin
  aqZapisz.SQL.Add(baseSQL1 + TS2[i] + baseSQL2);
end;
aqZapisz.Parameters.ParamByName('p1').Value := idk;
aqZapisz.Parameters.ParamByName('p3').Value := edtNrDok.Text;
aqZapisz.Parameters.ParamByName('p4').Value := edtPoz.Text;
aqZapisz.Parameters.ParamByName('p5').Value := id_grupy;;
aqZapisz.Parameters.ParamByName('p6').Value := id_rodzaju;
aqZapisz.Parameters.ParamByName('p7').Value := id_typu;
aqZapisz.Parameters.ParamByName('p8').Value :=
  DateToStr(zxDateDok.Date);
aqZapisz.Parameters.ParamByName('p9').Value :=
  DateToStr(zxDatePlat.Date);
aqZapisz.Parameters.ParamByName('p10').Value :=
  DateToStr(zxDateKsieg.Date);
aqZapisz.Parameters.ParamByName('p11').Value :=
    IntToStr(Integer(MonthOf(zxDateKsieg.Date)));
aqZapisz.Parameters.ParamByName('p12').Value :=
    IntToStr(Integer(YearOf(zxDateKsieg.Date)));
aqZapisz.Parameters.ParamByName('p13').Value :=
    RoundTo((StrToFloat(edtWartosc.Text) /
      listazaznprojektow.Count), -2);
aqZapisz.ExecSQL;

2)您可以创建存储过程并一次性发送所有数据。完全未经测试。

CREATE PROCEDURE (@p1 nvarchar(max), @p2 nvarchar(max), @p3 nvarchar(max), @p4 nvarchar(max), @p5 nvarchar(max), @p6 nvarchar(max), @p7 nvarchar(max), @p8 nvarchar(max), @p9 nvarchar(max), @p10 nvarchar(max), @p11 nvarchar(max), @p12 nvarchar(max), @p13 nvarchar(max))
AS
BEGIN
    DECLARE @idx1 INT;
    DECLARE @idx2 INT;
    SET @idx1=0;
    WHILE @idx1 >-1
    BEGIN;
        SELECT @idx2 = CHARINDEX(CHAR(13),@p2,@idx1);
        IF @idx2 > 0
        BEGIN;
            INSERT INTO projekty_koszty_rozb 
                  (id_kosztu, id_projektu, nr_dokumentu, pozycja, id_grupy, id_rodzaju, id_typu, data_dok, data_pla, data_ksi, mc,rok,kwota)
                  VALUES(@p1, SUBSTRING(@p2,@idx1,@idx2-@idx1), @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13)

            SET @idx1 = @idx2 + 2;
        END;
        ELSE
        BEGIN;
            INSERT INTO projekty_koszty_rozb 
                  (id_kosztu, id_projektu, nr_dokumentu, pozycja, id_grupy, id_rodzaju, id_typu, data_dok, data_pla, data_ksi, mc,rok,kwota)
                  VALUES(@p1, SUBSTRING(@p2,@idx1,LEN(@p2)+1-@idx1), @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13)

            SET @idx1 = -1;
        END;
    END;
END;

调用该过程并将p2stringlist传递为@ p2:

for i := 0 to TS2.Count - 1 do
begin
  p2stringlist.Add(TS2[i]);
end;

答案 3 :(得分:0)

您使用的是哪个版本的Delphi?您的数据库位于何处(WAN或LAN)? 如果你有像XE4-XE7这样的新版本,你可以尝试使用FireDAC FDQuery 它的Array DML功能(使用一组参数提交单个DBMS命令。)。

您的代码如下所示:

with FDQuery1 do begin
    SQL.Text := 'INSERT INTO projekty_koszty_rozb ' +
          '(id_kosztu,id_projektu, nr_dokumentu, pozycja,id_grupy, id_rodzaju, id_typu, data_dok, '
          + 'data_pla, data_ksi,mc,rok,kwota) ' +
          'VALUES(:p1,:p2, :p3, :p4, :p5, :p6, :p7, :p8, :p9,:p10,:p11,:p12,:p13)';
    // Set up parameter types
    Params[0].DataType := ftInteger;
    Params[1].DataType := ftString;
    Params[1].Size := 40;
    Params[2].DataType := ftString;
    Params[2].Size := 40;
    Params[3].DataType := ftString;
    Params[3].Size := 40;
    Params[4].DataType := ftString;
    Params[4].Size := 40;
    Params[5].DataType := ftInteger;
    Params[6].DataType := ftInteger;
    Params[7].DataType := ftInteger;    
    Params[8].DataType := ftString;
    Params[8].Size := 40;
    Params[9].DataType := ftString;
    Params[9].Size := 40;
    Params[10].DataType := ftString;
    Params[10].Size := 40;
    Params[11].DataType := ftString;
    Params[11].Size := 40;
    Params[12].DataType := ftString;
    Params[12].Size := 40;
    Params[12].DataType := ftFloat;

    // Set up parameters' array size
    Params.ArraySize := TS2.Count;
    // Set parameter values
    for i := 0 to TS2.Count - 1 do begin
      Params[0].AsIntegers[i] := idk;      
      Params[1].AsStrings[i] := TS2[i];
      Params[2].AsStrings[i] := edtNrDok.Text;
      Params[3].AsStrings[i] := edtPoz.Text;
      Params[4].AsIntegers[i] := id_grupy;
      Params[5].AsIntegers[i] := id_rodzaju;
      Params[6].AsIntegers[i] := id_typu;
      Params[7].AsStrings[i] := DateToStr(zxDateDok.Date);
      Params[8].AsStrings[i] := DateToStr(zxDatePlat.Date);
      Params[9].AsStrings[i] := DateToStr(zxDateKsieg.Date);
      Params[10].AsStrings[i] := IntToStr(Integer(MonthOf(zxDateKsieg.Date)));
      Params[11].AsStrings[i] := IntToStr(Integer(YearOf(zxDateKsieg.Date)));
      Params[11].AsDouble[i] := RoundTo((StrToFloat(edtWartosc.Text) / listazaznprojektow.Count), -2); 

    end;
    // Execute batch
    Execute(TS2.Count, 0);
  end;

您还可以尝试RemoteSQL,这会更快,并处理潜在网络上的慢性能。 您的基本代码可能如下所示:

procedure AddParams(aServerSQLParams: TRemoteSerializableSQLParams; aParamName: string; aFieldType: TFieldType; aValue: Variant);
var
  MyParam: TRemoteSerializableSQLParam;
begin
  MyParam := TRemoteSerializableSQLParam(aServerSQLParams.AddParameter);
  MyParam.Name := aParamName;
  MyParam.DataType := aFieldType;
  MyParam.Value := aValue;  
end;

procedure SaveMyData;
var
  MySerSQLParams: TRemoteSerializableSQLParams;
begin

 try
     RemoteSQL_Handler1.StartTransaction;
     MySerSQLParams:= TRemoteSerializableSQLParams.Create;

     for i := 0 to TS2.Count - 1 do begin

        MySQL := 'INSERT INTO projekty_koszty_rozb ' +
              '(id_kosztu,id_projektu, nr_dokumentu, pozycja,id_grupy, id_rodzaju, id_typu, data_dok, '
              + 'data_pla, data_ksi,mc,rok,kwota) ' +
              'VALUES(:p1,:p2, :p3, :p4, :p5, :p6, :p7, :p8, :p9,:p10,:p11,:p12,:p13)';

        AddParams(MySerSQLParams, 'p1', ftInteger, idk);
        AddParams(MySerSQLParams, 'p2', ftString, TS2[i]);
        AddParams(MySerSQLParams, 'p3', ftString, edtNrDok.Text);
        AddParams(MySerSQLParams, 'p4', ftString, edtPoz.Text);
        AddParams(MySerSQLParams, 'p5', ftInteger, id_grupy);
        AddParams(MySerSQLParams, 'p6', ftInteger, id_rodzaju);
        AddParams(MySerSQLParams, 'p7', ftInteger, id_typu);
        AddParams(MySerSQLParams, 'p8', ftString, DateToStr(zxDateDok.Date));
        AddParams(MySerSQLParams, 'p9', ftString, DateToStr(zxDatePlat.Date));
        AddParams(MySerSQLParams, 'p10', ftString, DateToStr(zxDateKsieg.Date));
        AddParams(MySerSQLParams, 'p11', ftString, IntToStr(Integer(MonthOf(zxDateKsieg.Date))));
        AddParams(MySerSQLParams, 'p12', ftString, IntToStr(Integer(YearOf(zxDateKsieg.Date))));
        AddParams(MySerSQLParams, 'p13', ftFloat, RoundTo((StrToFloat(edtWartosc.Text) / listazaznprojektow.Count), -2));

        RemoteSQL_Handler1.ExecuteSQL(MySQL, MySerSQLParams);
      end;
      RemoteSQL_Handler1.CommitTransaction;
  except
    RemoteSQL_Handler1.RollBackTransaction;
    FreeAndNil(MySerSQLParams);
  end;

  FreeAndNil(MySerSQLParams);
end;

RemoteSQL收集您的查询,压缩它们并立即将它们发送到服务器端(批处理)。