【问题标题】:MS Access Batch Update via ADO.Net and COM Interoperability通过 ADO.Net 和 COM 互操作性进行 MS Access 批量更新
【发布时间】:2013-03-25 04:24:17
【问题描述】:

这是this thread 的后续行动。这就是 .Net 2.0 的全部内容;至少对我来说。

基本上,Marc(来自上面的 OP)尝试了几种不同的方法来更新具有 100,000 条记录的 MS Access 表,并发现使用 DAO 连接比使用 ADO.Net 大约快 10 到 30 倍。我走了几乎相同的路径(下面的示例)并得出了相同的结论。

我想我只是想了解 为什么 OleDB 和 ODBC 慢得多,我很想知道是否有人找到了比 DAO 更好的答案自 2011 年那篇文章以来。我真的更愿意避免使用 DAO 和/或自动化,因为它们将要求客户端计算机具有可再分发的 Access 或数据库引擎(或者我坚持使用不支持的 DAO 3.6支持.ACCDB)。

最初的尝试; 100,000 条记录/10 列约 100 秒:

Dim accessDB As New OleDb.OleDbConnection( _ 
                      "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & _
                                accessPath & ";Persist Security Info=True;")
accessDB.Open()

Dim accessCommand As OleDb.OleDbCommand = accessDB.CreateCommand
Dim accessDataAdapter As New OleDb.OleDbDataAdapter( _
                                   "SELECT * FROM " & tableName, accessDB)
Dim accessCommandBuilder As New OleDb.OleDbCommandBuilder(accessDataAdapter)

Dim accessDataTable As New DataTable
accessDataTable.Load(_Reader, System.Data.LoadOption.Upsert)

//This command is what takes 99% of the runtime; loops through each row and runs 
//the update command that is built by the command builder. The problem seems to 
//be that you can't change the UpdateBatchSize property with MS Access
accessDataAdapter.Update(accessDataTable)

无论如何,我觉得这很奇怪,所以我尝试了几种相同的口味:

  • 为 ODBC 切换 OleDB
  • 循环遍历数据表并为每一行运行 INSERT 语句
    • 这就是 .Update 的作用
  • 使用 ACE 提供程序而不是 Jet(ODBC 和 OleDB)
  • 从 DataReader.Read 循环中运行数据适配器更新
    • 出于沮丧;太搞笑了。

最后,我尝试使用 DAO。代码基本上应该做同样的事情;但显然不是,因为它会在大约 10 秒内运行。

 Dim dbEngine As New DAO.DBEngine
 Dim accessDB As DAO.Database = dbEngine.OpenDatabase(accessPath)
 Dim accessTable As DAO.Recordset = accessDB.OpenRecordset(tableName)

While _Reader.Read
    accessTable.AddNew()
      For i = 0 To _Reader.FieldCount - 1
        accessTable.Fields(i).Value = _Reader.Item(i).ToString
      Next
    accessTable.Update()
End While

其他几点说明:

  • 所有示例中的所有内容都转换为字符串,以尽量保持简单和一致
    • 异常:在我的第一个示例中,我不使用 Table.Load 函数,因为...好吧,我真的不能,但是当我循环浏览阅读器并构建插入命令时,我做了基本相同的事情(无论如何,这就是它正在做的事情)。它没有帮助。
  • 对于每个字段...Next vs. Field(i) vs. Field(name) 对我来说没有区别
  • 我运行的每个测试都从一个新压缩的 Access 数据库中的空的预构建数据表开始
  • 将数据读取器加载到内存中的数据表大约需要 3 秒
  • 我认为封送数据不是问题,因为 Marc 的帖子表明通过自动化加载文本文件的速度与 DAO 一样快——如果有的话,它不应该在使用 ODBC/OleDB 时封送数据,但它应该在使用自动化时
  • 所有这一切都让我感到不安,因为它没有任何意义

希望有人能够对此有所了解……这很奇怪。 提前致谢!

【问题讨论】:

    标签: .net performance ms-access ado.net dao


    【解决方案1】:

    这里的原因是,DAO 驱动程序比 ODBC 驱动程序更接近 MS Access 数据库引擎。

    DAO 方法 AddNewUpdate 直接委托给 MS Access 等效项,它绝不会生成 SQL,因此 MS Access 不会解析任何 SQL。

    另一方面,DataAdapter 代码为每一行生成一个更新语句,该更新语句被传递给 ODBC,然后 ODBC 再将其传递给 MSAccess 驱动程序,该驱动程序要么

    1. 独立解析SQL并发出AddNewUpdate Access 数据库的命令或
    2. 将 SQL 传递给 MS Access,它没有为解析 SQL 进行优化, 并且一旦解析,最终将 SQL 转换为 AddNewUpdate 命令。

    无论哪种方式,您都需要花费时间生成 SQL,然后让某些东西解释该 SQL,其中 DAO 方法绕过 SQL 生成/解释并直接进入金属。

    解决此问题的一种方法是创建您自己的“数据库服务”,该服务在具有访问数据库的机器上运行。这会编组您的选择和更新,并可以通过远程处理、WCF(http 或其他)与客户端进行通信。这是很多工作,并且会极大地改变您的应用程序逻辑。

    找出数据库驱动程序的正确名称(例如 Jet 或其他)是留给读者的练习

    【讨论】:

    • 感谢您的解释!我知道 DAO 直接位于 DB 引擎上,我只是从未见过 仅因使用额外的中间件层而对性能造成如此大的影响。在这种情况下,与其他 DBMS 相比,Access 的主要目标是 Update 通常会分批运行更新,而不是通过 ODBC 为每条记录传递吗?
    • 命中率太糟糕了,因为它是 Access。对于real RDBMS(这里的火焰诱饵)不会那么糟糕,对于 SQL Server(和 SQL Server express),您可以使用SqlBulkCopy 类,它以接近面融化的速度插入行.如果可以的话,老实说,我建议放弃 MS Access 并使用 SQL Express 运行。我以前广泛使用 Access,但从不喜欢它。 “我很高兴我为那个项目使用了访问权限”从来没有人说过
    • 我不反对;但是,您知道,旧版支持所有东西
    【解决方案2】:

    我知道这个问题很老,但答案可能会帮助仍在为此苦苦挣扎的人。

    还有另一种方法可以考虑。由于源连接字符串和目标连接字符串都是已知的,源表可以链接到目标 Access 数据库,可能需要通过 DAO 或 ADOX 进行一些连接字符串解析(我知道,ADOX 在这里是题外话)。
    然后,通过在与目标 Access 数据库的 DAO 或 OleDb 连接上发出类似这样的语句,可以相当快地传输如此链接的表中的数据:

    SELECT * INTO Table1 FROM _LINKED_Table1
    

    一些缺点(请指出我遗漏的任何地方):

    • 源表必须包含主键
    • 必须通过检查源索引架构重新创建主键和索引
    • 查询运行时不易获取传输进度状态

    一些优点(请指出我遗漏的任何内容):

    • 如果要复制所有用户表,则只需检查源表架构
    • 不必检查源列架构来为 CREATE TABLE 语句生成列定义
      (例如,尝试从 OleDb 架构中可靠地获取 AUTONUMBER / IDENTITY 信息,即不假设列值和标志的组合位基于检查其他模式)
    • 不必生成大量的 INSERT INTO ... VALUES ... 语句,占代码中的 AUTONUMBER / IDENTITY 列,或者以其他方式为代码中的每一行运行数据库操作
    • 能够指定过滤传输记录的条件
    • 不必担心文本、日期或时间列或如何在查询中分隔、转义或格式化它们的值,除非在查询条件中使用

    这种方法被用于一个生产项目,结果证明是最快的,至少对我来说是这样。 :o)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-10
      • 1970-01-01
      • 1970-01-01
      • 2011-02-21
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      相关资源
      最近更新 更多