Sql server Delphi-MS SQL 2014中的多个循环插入

Sql server Delphi-MS SQL 2014中的多个循环插入,sql-server,delphi,insert,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

我需要循环抛出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个项目。如何提高速度???

不要每次循环都替换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;

最大的影响是您正在一个接一个地执行多个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;

您可以通过以下方式提高速度:

  • 单笔交易

    Connection.StartTransaction;
    尝试
    //将数据发送到服务器
    连接。提交;
    除了
    连接回滚;
    结束;
    
  • 将数据分为固定部分和动态部分,这样就不必一次又一次地向服务器发送相同的数据。将固定零件存储到临时表中,并在每次插入动态数据时使用它

  • 重用已经准备好的查询->不要在循环中设置sql语句,或者清除它以重新设置它
  • 重新使用已设置的固定参数(2的备选方案)
  • 一个快速的解决办法是

    aqZapisz.Connection.StartTransaction;
    尝试
    aqZapisz.SQL.Text:=
    “插入项目中”+
    "(id_kosztu,id_projektu,nr_dokumentu,pozycja,id_grupy,id_rodzaju,id_typu,data_dok)"+
    ‘数据解放军、数据ksi、mc、韩国、广岛)’+
    '值(:p1,:p2,:p3,:p4,:p5,:p6,:p7,:p8,:p9,:p10,:p11,:p12,:p13)';
    aqZapisz.Parameters.ParamByName('p1')。值:=idk;
    aqZapisz.Parameters.ParamByName('p3')。值:=edtNrDok.Text;
    aqZapisz.Parameters.ParamByName('p4')。值:=edtPoz.Text;
    aqZapisz.Parameters.ParamByName('p5')。值:=id_grupy;;
    aqZapisz.Parameters.ParamByName('p6')。值:=id_rodzaju;
    aqZapisz.Parameters.ParamByName('p7')。值:=id_typu;
    aqZapisz.Parameters.ParamByName('p8')。值:=DateToStr(zxDateDok.Date);
    aqZapisz.Parameters.ParamByName('p9')。值:=DateToStr(zxDatePlat.Date);
    aqZapisz.Parameters.ParamByName('p10')。值:=DateToStr(zxDateKsieg.Date);
    aqZapisz.Parameters.ParamByName('p11')。值:=IntToStr(整数(MonthOf(zxDateKsieg.Date));
    aqZapisz.Parameters.ParamByName('p12')。值:=IntToStr(整数(YearOf(zxDateKsieg.Date));
    aqZapisz.Parameters.ParamByName('p13')。值:=RoundTo((StrToFloat(edtWartosc.Text)/listazaznprojektow.Count),-2);
    对于i:=0到TS2。计数-1 do
    开始
    aqZapisz.Parameters.ParamByName('p2')。值:=TS2[i];
    aqZapisz.ExecSQL;
    结束;
    aqZapisz.Connection.Commit;
    除了
    aqZapisz.Connection.Rollback;
    提高;
    结束;
    

    您应该测量应用程序和服务器任务需要多长时间才能得到提示,是否值得实现(2)

    对于这种情况,较新的Delphi版本有一个方便的解决方案

    var
    LInternal,LServer:TStopWatch;
    开始
    LServer:=TStopWatch.Create;
    LInternal:=TStopWatch.StartNew;
    ...
    对于i:=0到TS2。计数-1 do
    开始
    aqZapisz.Parameters.ParamByName('p2')。值:=TS2[i];
    LServer.Start;
    aqZapisz.ExecSQL;
    LServer.Stop;
    结束;
    ...
    停下来;
    ShowMessage(
    格式(
    '总计:%dms服务器:%dms',
    [LInternal.elapsedmilloses,LServer.elapsedmilloses]);
    结束;
    
    您使用哪个版本的Delphi?您的数据库位于哪里(广域网或局域网)? 如果您有像XE4-XE7这样的更新版本,可以尝试使用FireDAC FDQuery 以及它的特性(提交一个带有参数数组的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;
    
    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;
    
    您还可以尝试在潜在网络上更快、处理较慢性能的方法。 您的基本代码可能如下所示:

    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;
    
    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收集您的查询,压缩它们并立即(批处理)将它们发送到服务器端。

    执行一些大容量插入搜索。尝试使用所述的外部表,例如,此处:。它的工作速度比按一行解释数据快得多。顺便说一句,它只在服务器端工作。似乎只有参数p2被更改,除非我忽略了什么。我认为在循环之前移动除p2之外的所有参数可以使它更加高效。我测试了这两个建议-循环中的所有参数,循环中只有p2。结果=可能比旧代码好5秒,但仍然太长;(可能可以发送到sql server命令和所有参数(使用ts2)并将其插入循环中,但“在”sql server中而不通过网络传输每个插入?类似于使用for循环的存储过程…@user1762186也尝试按名称取消对参数的访问(每次Delphi在参数列表中搜索时)。将一些变量声明为
    p1,p2,…:TParam;
    ,然后在循环
    p1:=aqZapisz.Parameters.ParamByName('p1')…
    之前,使用
    p1.Value:=…
    而不是
    aqZapisz.Parameters.ParamByName('p1')。值:=…
    在循环中。值(@p1,SUBSTRING(@p2,@idx1,LEN(@str)中的“@str”是什么+1-@idx1、@p3、@p4、@p5、@p6、@p7、@p8、@p9、@p10、@p11、@p12、@p13)`因为is未声明?我尝试了第一种方法,但结果是一样的。执行代码的最长部分是for循环-它需要很长的时间:(@user1762186
    @str