【问题标题】:Insert many records using ADO使用 ADO 插入多条记录
【发布时间】:2010-03-23 22:05:50
【问题描述】:

我正在寻找使用 ADO 一次向表中插入多条记录 (+1000) 的最快方法。

选项:

  1. 使用插入命令和参数

    ADODataSet1.CommandText:='INSERT INTO .....';    
    ADODataSet1.Parameters.CreateParameter('myparam',ftString,pdInput,12,''); 
    ADODataSet1.Open;
    
  2. 使用TAdoTable

    AdoTable1.Insert;
    AdoTable1.FieldByName('myfield').Value:=myvale;
    //..
    //..
    //..
    AdoTable1.FieldByName('myfieldN').value:=myvalueN;
    AdoTable1.Post;
    
  3. 我正在使用 delphi 7、ADO 和 ORACLE。

【问题讨论】:

    标签: delphi ado


    【解决方案1】:

    您最快的方法可能是选项 2。插入所有记录并告诉数据集将其发送到数据库。但是 FieldByName 很慢,你可能不应该在这样的大循环中使用它。如果您已经拥有这些字段(因为它们是在设计时定义的),请在代码中引用这些字段的实际名称。如果没有,请为每个字段调用一次 FieldByName 并将它们存储在局部变量中,并在插入时通过这些变量引用字段。

    【讨论】:

    【解决方案2】:

    使用 ADO 我认为您可能不走运。并非所有后端都支持批量插入操作,因此 ADO 实现了一种抽象,以允许对明显批量操作(批次)进行一致编码,而不管后端支持“在后台”仅插入“批处理”作为一大堆参数化的单独插入。

    这样做的缺点是,即使是那些支持批量插入的后端也并不总是将其编码到他们的 ADO/OLEDB 提供程序中——何必呢? (我看到它提到 Oracle OLEDB 提供程序支持批量操作,并且 ADO 拒绝访问它,因此 ADO 框架甚至可能根本不允许提供程序在 ADO 本身中更直接地支持此功能 -我不确定)。

    但是,您提到了 Oracle,而这个后端确实确实通过其原生 API 支持批量插入操作。

    There is a commercial Delphi component library - ODAC (Oracle Direct Access Components) for, um, direct access to Oracle (it does not even require the Oracle client software to be installed).

    这还直接支持 Oracle 提供的批量插入功能,并且是访问 Oracle 数据存储的一种高效方法。

    【讨论】:

    • 您可以免费使用我们的开源 Oracle 单元实现高性能,从 Delphi 5 到 XE2。它不使用ADO,而是直接调用Oracle Client。见blog.synopse.info/post/2012/07/19/…
    【解决方案3】:

    您正在尝试执行的操作称为bulk insert。 Oracle 提供了可用于此目的的 .NET 程序集 Oracle.DataAccess.dll没有任何一种手工解决方案可以胜过这个针对 Oracle DBMS 的自定义供应商库的性能。

    http://download.oracle.com/docs/html/E10927_01/OracleBulkCopyClass.htm#CHDGJBBJ

    http://dotnetslackers.com/articles/ado_net/BulkOperationsUsingOracleDataProviderForNETODPNET.aspx

    最常见的想法是为每一列使用值数组并将它们应用于模板 SQL。在下面的示例中,employeeIds、firstNames、lastNames 和 dobs 是与要插入的值长度相同的数组。

    ODP.NET 中的数组绑定功能 允许您插入多条记录 在一次数据库调用中。使用数组 绑定,你只需设置 OracleCommand.ArrayBindCount 到 要插入的记录数,以及 将值数组作为参数传递 而不是单个值:

    > 01. string sql =
    > 02. "insert into bulk_test (employee_id, first_name, last_name,
    > dob) "
    > 03.
    > + "values (:employee_id, :first_name, :last_name, :dob)";
    > 04.
    >  
    > 05. OracleConnection cnn = new OracleConnection(connectString);
    > 06. cnn.Open();
    > 07. OracleCommand cmd = cnn.CreateCommand();
    > 08. cmd.CommandText = sql;
    > 09. cmd.CommandType = CommandType.Text;
    > 10. cmd.BindByName = true;
    > 11.
    >  
    > 12. // To use ArrayBinding, we need to set ArrayBindCount
    > 13. cmd.ArrayBindCount = numRecords;
    > 14.
    >  
    > 15. // Instead of single values, we pass arrays of values as parameters
    > 16. cmd.Parameters.Add(":employee_id", OracleDbType.Int32,
    > 17. employeeIds, ParameterDirection.Input);
    > 18. cmd.Parameters.Add(":first_name", OracleDbType.Varchar2,
    > 19. firstNames, ParameterDirection.Input);
    > 20. cmd.Parameters.Add(":last_name", OracleDbType.Varchar2,
    > 21. lastNames, ParameterDirection.Input);
    > 22. cmd.Parameters.Add(":dob", OracleDbType.Date,
    > 23. dobs, ParameterDirection.Input);
    > 24. cmd.ExecuteNonQuery();
    > 25. cnn.Close();
    

    如您所见,代码看起来并没有太大的不同 从做一个常规的单记录 插入。然而,表现 改善是相当剧烈的, 取决于记录的数量 涉及。您必须记录的记录越多 插入,性能越大 获得。在我的开发 PC 上,插入 使用数组绑定的 1,000 条记录是 比插入快 90 倍 一次记录一个。是的,你读过 没错:快 90 倍!您的 结果会有所不同,具体取决于 记录大小和网络 到数据库的速度/带宽 服务器。

    一些调查工作揭示了 该 SQL 被认为是 “执行”多次 服务器端。证据来自 V$SQL(查看 EXECUTIONS 列)。 但是,从 .NET 的角度来看, 一切都在一个电话中完成。

    【讨论】:

    • 很高兴知道这一点,但有点无关紧要,因为 Delphi 不是 .NET 语言。
    • Oracle 调用接口 (OCI) 是一个可以用多种语言包装的 C++ 库 - 上面我展示了一个 .NET 示例。我想Delphi应该有一些图书馆? oracle.com/technology/tech/oci/index.html
    • 这里似乎是付费解决方案。但永远不要为你可以免费拥有的东西付费,所以先搜索一下。 devart.com/odac
    • OCI 是“C”库,OCCI 是 C++ 版本。扭曲 C++ 代码比 C 更难。有 Delphi 包装器,即 DOA(直接 Oracle 访问)和 ODAC。
    【解决方案4】:

    直接使用 TADOConnection 对象确实可以提高插入性能。

    dbConn := TADOConnection......
    dbConn.BeginTrans;
    try
      dbConn.Execute(command, cmdText, [eoExecuteNoRecords]);
      dbConn.CommitTrans;
    except
      on E:Exception do
      begin
        dbConn.RollbackTrans;
        Raise e;
      end;
    end;
    

    另外,一次插入多条记录可以进一步提高速度。

    【讨论】:

    • 我发现直接执行 SQL 而不是通过表进行插入要快得多。
    【解决方案5】:

    您也可以尝试 TADODataset 的 BatchOptmistic 模式。我没有 Oracle,所以不知道 Oracle 是否支持它,但我在 MS SQL Server 上使用过类似的。

    ADODataSet1.CommandText:='select * from  .....';
    ADODataSet1.LockType:=ltBatchOptimistic;
    ADODataSet1.Open;
    
    ADODataSet1.Insert;
    ADODataSet1.FieldByName('myfield').Value:=myvalue1;
    //..
    ADODataSet1.FieldByName('myfieldN').value:=myvalueN1;
    ADODataSet1.Post;
    
    ADODataSet1.Insert;
    ADODataSet1.FieldByName('myfield').Value:=myvalue2;
    //..
    ADODataSet1.FieldByName('myfieldN').value:=myvalueN2;
    ADODataSet1.Post;
    
    ADODataSet1.Insert;
    ADODataSet1.FieldByName('myfield').Value:=myvalue3;
    //..
    ADODataSet1.FieldByName('myfieldN').value:=myvalueN3;
    ADODataSet1.Post;
    
    
    // Finally update Oracle with entire dataset in one batch
    ADODataSet1.UpdateBatch(arAll);
    

    【讨论】:

      【解决方案6】:

      1000 行可能不是这种方法变得经济的点,但考虑将插入写入平面文件,然后运行 ​​SQL*Loader 命令行实用程序。这确实是将数据批量上传到 Oracle 的最快方式。

      http://www.oracleutilities.com/OSUtil/sqlldr.html

      我见过开发人员花费数周时间编写 (Delphi) 加载例程,其执行速度比由控制文件控制的 SQL*Loader 慢几个数量级。

      【讨论】:

        【解决方案7】:

        请记住禁用链接到数据集/表/查询/...的可能控件。

        ...
        ADOTable.Disablecontrols;
        try
           ...
        finally
           ADOTable.enablecontrols;
        end;
        ...
        

        【讨论】:

          【解决方案8】:

          您可以尝试 Append 而不是 Insert:

          AdoTable1.Append;
          AdoTable1.FieldByName('myfield').Value:=myvale;
          //..
          //..
          //..
          AdoTable1.FieldByName('myfieldN').value:=myvalueN;
          AdoTable1.Post;
          

          使用 Append,您将在客户端数据集上节省一些精力,因为记录将被添加到末尾,而不是插入记录并将其余部分向下推。

          此外,您可以取消挂钩任何可能绑定到数据集的数据感知控件或使用 BeginUpdate 锁定它们。

          我从 append 方法中获得了相当不错的性能,但是如果您期望批量速度,您可能希望通过执行查询本身来查看在单个查询中插入多行,如下所示:

          AdoQuery1.SQL.Text = 'INSERT INTO myTable (myField1, myField2) VALUES (1, 2), (3, 4)';
          AdoQuery1.ExecSQL;
          

          一次插入多条记录时,您应该从数据库引擎中获得一些好处。

          【讨论】:

            猜你喜欢
            • 2011-07-05
            • 1970-01-01
            • 2015-09-25
            • 2016-07-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-12-31
            相关资源
            最近更新 更多