6

我需要使用 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 可能是首先将光标定位在正确的位置然后写入的问题。

不确定。可能会有专家告诉我一条出路。

顺便提一句。我还尝试用固定长度的循环替换 Loop Until Data1(1).Recordset.EOF = True 语句,因为我还读到,这种对 Recordset.EOF 的检查也会降低性能。

先感谢您!

4

4 回答 4

2

我创建了一个名为test字段的表,n并且f(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 机器 + 字段上没有索引

于 2013-06-20T14:37:20.143 回答
1

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

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

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

然后,您可以根据您的PK划分数据,并更新第一组中的所有记录,然后在第二个,第三个......

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

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

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

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

于 2013-06-21T13:45:39.853 回答
1

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

这可以工作的三种情况:

如果可以从其他字段计算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 sec [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
于 2013-06-20T12:59:26.400 回答
0

为了提高性能,您可以使用UpdateBatchADODB Object 的方法。但要使用此功能,它需要:

  1. adOpenStaticcursorType
  2. adLockBatchOptimistic锁定类型

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

更进一步,不要使用rec.EOF测试。您应该改为使用 for 循环从1 to rec.RecordCount

Worth mentioning:rec.RecordCount在 ADODB 遍历所有记录之前,读取是不可靠的。所以做一个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我从 切换到已经超过 3 年了PHP。这里可能缺少一些曲目。Please note:我还没有执行这个代码。它可能包含一些小问题,但这应该足以用于指示目的。

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

于 2013-06-24T23:33:35.993 回答