9

这是对这个线程的一种跟进。这就是.Net 2.0的全部内容;至少对我来说。

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

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

最初的尝试;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 时封送数据,但应该使用自动化时
  • 所有这些都让我感到不安,因为它没有意义

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

4

2 回答 2

6

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

DAO 方法AddNewUpdate直接委托给 MS Access 等效项,它在任何时候都不会生成 SQL,因此 MS Access 没有要解析的 SQL。

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

  1. AddNew独立地将 SQL 和问题和Update 命令解析到 Access 数据库或
  2. 将 SQL 传递给 MS Access,它没有针对解析 SQL 进行优化,一旦解析,最终会将 SQL 转换为AddNew命令 Update

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

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

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

于 2013-04-03T14:04:42.560 回答
2

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

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

SELECT * INTO Table1 FROM _LINKED_Table1

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

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

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

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

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

于 2014-10-09T20:19:08.090 回答