【问题标题】:VB6 Ms Access Database Editing large amount of recordsVB6 Ms Access数据库编辑大量记录
【发布时间】:2013-06-17 07:16:07
【问题描述】:

我需要使用 VB6 和 MS Access 数据库处理数十万条记录。我遍历记录集并编辑每条记录。然而,这样做需要很多时间。使用 Addnew 和 Update 方法创建具有相同数量记录的数据库的速度要快得多。

如果有人向我展示任何代码示例或只是一个策略,我将不胜感激。

这里是代码

Data1(1).RecordSource = "Select * from TABLE order by Field_A ASC"
Data1(1).Refresh
If Data1(1).Recordset.RecordCount > 0 Then
    Data1(1).Recordset.MoveFirst
    Do
        Data1(1).Recordset.Edit
        Data1(1).Recordset.Fields("FIELD") = Sort_Value
        Data1(1).Recordset.Update
        Data1(1).Recordset.MoveNext
    Loop Until Data1(1).Recordset.EOF = True
End If

这真的很简单。真正的事情是,我忘了提到,计算机的硬盘不断红/写。这实际上是问题所在。负载如此之大,无法不影响性能。

我首先想到的是查询生成的记录集,记住我们有 1-2 百万条记录,导致了这个问题。我猜它位于硬盘驱动器和 RAM 中的某个临时位置。所以执行 .Edit 和 .Update 可能是先将光标定位在正确的位置然后写入的问题。

不确定。可能会有专家给我指路。

顺便说一句。我也尝试更换 循环直到 Data1(1).Recordset.EOF = True 具有固定长度循环的语句,因为我也读到过,这种对 Recordset.EOF 的检查也会降低性能。

提前谢谢你!

【问题讨论】:

  • 分享您的代码、UPDATE 语句等会有所帮助
  • 使用事务而不是每次都编写更新会明显更快,请在此处查看我的示例答案:stackoverflow.com/questions/12930603/…
  • 我为你开始了赏金,因为我自己对此很好奇。如果您可以编辑您的帖子以包含您当前使用的代码,它可能会帮助人们发现您的代码的弱点。
  • 如果你不向我们展示(或至少告诉)你在做什么,你真的很难说你应该如何提高你正在做的事情的速度首先。 “我怎样才能让我的代码更快?我不会向你展示它,甚至不会告诉你它究竟做了什么。你能帮帮我吗?”
  • 显然您正在使用带有 DAO 的数据控件来更新您的记录。摆脱那个数据控制,只使用普通的记录集。如果不需要,也删除Order By。然后重试。

标签: ms-access vb6


【解决方案1】:

我创建了一个名为test 的表,其中包含nf(n) 字段

定时 3 个不同的更新子例程 - 没有事务的记录集 - 带有事务的记录集 - 更新查询

Sub updateFunction_noTrans()
    Dim rs As Recordset
    Set rs = CurrentDb.OpenRecordset("test")
    rs.MoveFirst
    Do Until rs.EOF
        rs.Edit
        rs("f(n)") = rs("n") + 1
        rs.Update
        rs.MoveNext
    Loop
End Sub

这基本上就是你正在做的,编辑字段时的直接记录集

Sub updateFunction_yesTrans()
    Dim i As Long
    Dim commitSize As Long
    Dim rs As Recordset
    commitSize = 5000
    Set rs = CurrentDb.OpenRecordset("test")
    DBEngine.Workspaces(0).BeginTrans
    rs.MoveFirst
    Do Until rs.EOF
        rs.Edit
        rs("f(n)") = rs("n") + 1
        rs.Update
        rs.MoveNext
        i = i + 1
        If i = commitSize Then
            DBEngine.Workspaces(0).CommitTrans
            DBEngine.Workspaces(0).BeginTrans
            i = 0
        End If
    Loop
    DBEngine.Workspaces(0).CommitTrans
End Sub

这是相同的想法,但与交易有关。我一次提交 5000 条记录,因为每次提交有一些限制在 9k-10k 左右。我相信你可以通过进入注册表来编辑它。

Sub updateFunction_updateQuery()
    CurrentDb.Execute ("UPDATE test SET test.[f(n)] = [n]+1;")
End Sub

这比任何记录集方法都快。例如。在大约 200 万条记录上,没有事务大约需要 20 秒,事务大约需要 18-19 秒,更新查询大约需要 14 秒。

这一切都假设要更新的字段取决于从该记录中的其他恶魔计算的值

要真正加快这类行动,有时取决于具体情况,如果不适用,则需要更多细节。

编辑:使用旧的 core 2 duo 机器 + 字段上没有索引

【讨论】:

  • +1 用于建议交易。如果正在更新的字段上有索引,那么我希望看到显着的改进。您还可以通过在 VBA 代码中使用类似 DBEngine.SetOption dbMaxLocksPerFile, 300000 的语句来避免编辑注册表,然后您可以将 commitSize 增加到 ~250,000。
  • @Gord,实际上恰恰相反。我已经玩了几个小时了,并且已经编写了几个不同的测试。对于 100 万条记录,没有索引,无论是否使用事务,我都会得到 11 秒。对于索引字段(文本),我得到 27 秒使用事务和 25 秒没有事务。顺便说一句,提交大小为 5000。
  • 感谢您在前夜测试您的代码。真的很感激。但是有一个问题——可用的最大提交大小是多少?
  • 我没有尝试查看最高级别是多少。 @GordThompson 的评论表明 250k 是可能的。从速度的角度来看,我个人认为事务没有好处,主要好处是能够回滚更改等。
【解决方案2】:

我唯一的建议是使用更新查询进行大规模更新,这可能不适用于您的情况。

这可以工作的三种情况:

如果 Sort_Value 可以从其他字段中计算出来,那就是一个简单的 UPDATE 查询,但我相信您已经看到了。

如果 Sort_Value 可以从其他记录(如以前的记录)中计算出来,那么您可能可以编写更复杂的 UPDATE 查询(我在这里看到了一些非常复杂的查询)。

最后,如果相同的 Sort_Value 被应用于许多记录,那么您可以根据这些记录发出 UPDATE 查询。因此,如果 Sort_Value 可以是 10 个不同的值,那么您的所有更新都将在 10 个 UPDATE 查询中完成。


如果您告诉我们您从哪里获得 Sort_Value,我们或许可以为您提供进一步的帮助。


根据我的测试,可以加快编辑/更新命令的速度。这一切都是使用一个包含 10,000 条记录和 1,000,000 次更新的表完成的。

  • RS(1) 而不是 RS("name")。这是在另一个网站上提出的,并且 实际上增加了 20% 的时间。 (25 秒 / 21 秒)
  • BeginTrans/CommitTrans 对未索引的字段没有影响,并且 在索引字段上快 1%。 (未编入索引:11 秒 [w/ trans] / 11 秒,索引:23 秒 [w/ trans] / 25 秒)*
  • 单独的 SQL 语句。 (86 秒)
  • 参数查询定义。 (43 秒)

*修正结果。


BeginTrans/CommitTrans 测试的代码。

Sub CommitTest()
   Dim C As String
   Dim I As Long
   Dim J As Long
   Dim RS As Recordset
   Dim BegTime As Date
   Dim EndTime As Date
   BegTime = Now()
   Set RS = CurrentDb.OpenRecordset("tblTest")
   For J = 1 To 200
      RS.MoveFirst
      DBEngine.Workspaces(0).BeginTrans
      For I = 1 To 5000
         C = Chr(Int(Rnd() * 26 + 66))
         RS.Edit
         RS("coltest") = C
         RS.Update
         RS.MoveNext
      Next I
      DBEngine.Workspaces(0).CommitTrans
   Next J
   EndTime = Now()
   Debug.Print DateDiff("s", BegTime, EndTime)
End Sub

【讨论】:

  • +1 用于测试,但 YMMV。对于我的特定测试用例——一个包含 100 万行的表中的每一行更新一个 DAO 记录集——使用事务总是可以提高性能,但只提高了大约 17%。
  • 谢谢。我实际上纠正了我的结果,因为我没有对 BeginTrans/CommitTrans 进行公平的测试(意外地)。在索引字段上仍然只快 1%。我发布了我上面使用的代码。如果您知道为什么您的测试速度如此之快,请告诉我。
  • 是的,基本上我认为它归结为避免记录集并尽可能使用查询。无论我们如何优化记录集都不会接近 querydefs。
  • 我同意。我想不出在查询中无法以某种方式计算 Sort_Value 的任何情况,即使它调用了外部函数。
【解决方案3】:

虽然在某些情况下可能有必要,但应避免遍历记录集以更新字段。

更有效的明智做法是编写 SQL 更新查询。

如果你的表太大,你必须小心选择索引,特别是主键。

然后,你可以根据你的PK划分数据,更新第一组的所有记录,然后第二组,第三组……

 UPDATE super_big_table
 SET Field_A = some_function_to_make_it_sort_value
 WHERE myPrimaryKey BETWEEN ( left_boundary AND right_boundary )

您对您在表格中所做的所有划分重复此操作(通过代码)。

现在,问题是——你能想到一个 Access 函数来创建所需的 Sort_value 吗?

请注意,如果 Field_A 是您的主键,则不应更改它。否则,每次更新几条记录时,您的所有表格都会重新排列,这对您的 HD / 处理器来说将是很多工作。在这种情况下,您应该有一个不同的 PK,并在 Field_A 而不是 PK 上创建索引。

【讨论】:

    【解决方案4】:

    为了提高性能,您可以使用 ADODB 对象的UpdateBatch 方法。但要使用此功能,它需要:

    1. adOpenStatic cursorType
    2. adLockBatchOptimisticLockType

    在记录集对象上。
    此外,您还可以使用adUseClientCursorLocation 在操作期间在客户端而不是服务器上加载。

    为了更进一步,不要使用rec.EOF 测试。你应该使用从1 to rec.RecordCount开始的for循环

    Worth mentioning: 在 ADODB 遍历所有记录之前,读取rec.RecordCount 是不可靠的。所以做一个MoveLastMoveFirst 以确保正确的记录计数。

    使用以下代码作为提示:

    set con = Server.CreateObject("ADODB.Connection")
    con.Provider = "Microsoft.Jet.OLEDB.4.0"
    con.Open(Server.Mappath("MyShopDB.mdb"))
    set rec = Server.CreateObject("ADODB.recordset")
    sql = "SELECT * FROM Employees"
    
    rec.CursorLocation = adUseClient
    rec.CursorType = adOpenStatic
    rec.LockType = adLockBatchOptimistic
    
    rec.Open sql, con
    
    if not rec.EOF then            ' rescue no records situation
        rec.moveLast               ' let it see all records
        rec.moveFirst
    end if
    
    cnt = rec.RecordCount          ' avoid reading each time in loop test
    
    if cnt > 0 then
        for i = 1 to cnt
            rec.Fields("FIELD").value = Sort_Value
            '...
            '...
            '...
            rec.MoveNext
        next i
        rec.UpdateBatch
    end if
    
    rec.Close
    con.Close
    

    我从 VB 切换到 PHP 已经超过 3 年了。这里可能缺少一些曲目。 Please note:我还没有执行这段代码。它可能包含一些小问题,但这应该足以用于指示目的。

    您还可以尝试将批次拆分为片段以查看对性能的影响。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多