【问题标题】:How to speed up bulk 'upsert' in Microsoft Access如何加快 Microsoft Access 中的批量“更新插入”
【发布时间】:2019-01-03 03:31:34
【问题描述】:

我正在使用 VBA 字典在 Access 中执行“更新插入”。我正在循环我的字典键,如果该键存在于 Access 表中,那么我将更新它。如果没有,它会插入它。然而,我的表包含 200k+ 行,这使得代码执行速度非常慢(例如,5 分钟甚至 5% 都没有完成),因为迭代需要在整个表中搜索“LOC”(我的键)。

有没有办法让我显着加快这个过程? 我的代码如下,感谢任何帮助。

Sub UpdateDatabase(dict As Object)

Dim db As Database
Dim rs As DAO.Recordset

Set db = OpenDatabase("C:\XXX\myDB.accdb")
Set rs = db.OpenRecordset("MyTable", dbOpenDynaset)


For Each varKey In dict.Keys()

    'Table is searched for key
    rs.FindFirst "[LOCID] = '" & varKey & "'"
    If rs.NoMatch Then
        'If the key was not found, insert it
        rs.AddNew
        rs!LOCID = varKey
        rs![Status] = "To Start"
        rs.Update
    Else
        'If the key was found, update its status
        rs.Edit
        rs![Status] = "Done"
        rs.Update
    End If
Next

rs.Close
db.Close

Application.StatusBar = False
End Sub

编辑:

我在上面的代码中发现了瓶颈。这是行:

rs.FindFirst "[LOCID] = '" & varKey & "'"

这用于查找密钥是否在数据库中。删除它(并简单地插入新数据)加快了这个过程,并且在几秒钟内完成。有没有一种快速的方法来确定一个值是否已经在一个表中?

【问题讨论】:

  • 您可以定期调用 UpdateBatch,而不是每次更改后...您的字典中有多少条目?
  • @TimWilliams 我发现性能瓶颈实际上并不在于添加数据,而在于确定该值是否已在表中的代码。我已经编辑了我的问题。
  • Upserting in MS-access 的可能重复项。请参阅描述查询连接表的答案。
  • @June7 我相信我所针对的方法不同,足以保证提出一个新问题。我的目标是使用 DAO 提高性能。
  • DAO 循环将比查询慢得多。如果需要性能,请使用查询,否则,如果是教程示例或性能实验,请确保键列有索引并使用Seek方法而不是FindFirst

标签: excel vba ms-access dao


【解决方案1】:

首先: dict 集合有多大。 (5、10 或 1000?)。

接下来,LOCID 是该表“MyTable”中的索引列。

如果 LOCID 已经是一个 N 索引列,那么您可以使用以下代码。它应该可以将速度提高大约 100 倍或更多倍:

假设该表不是链接表,实际上是数据库中的一个表。

请注意,您必须将有问题的表打开为“可操作”——这是默认设置,因此我删除了 dbOpenDynaset)

这段代码可以解决问题:

Dim db        As DAO.Database
Dim rs        As DAO.Recordset


Set db = OpenDatabase("C:\XXX\myDB.accdb")
Set rs = db.OpenRecordset("MyTable")     ' <--- note this!!!

rs.Index = "LOCID"

For Each varKey In dict.Keys()

  'Table is searched for key
  rs.Seek "=", varKey
  If rs.NoMatch = True Then
      'If the key was not found, insert it
      rs.AddNew
      rs!LOCID = varKey
      rs![Status] = "To Start"
      rs.Update
  Else
      'If the key was found, update its status
      rs.Edit
      rs![Status] = "Done"
      rs.Update
  End If
Next

rs.Close
db.Close

您还需要确定 LOCID 列上实际索引的名称。我在上面使用了 LOCID,但您最好使用访问权限打开数据库,将表格翻转到设计模式,然后点击功能区“索引”按钮。如果 LOCID 是主键,那么索引很有可能被命名为 PrimaryKey。所以你需要索引的名称。

【讨论】:

    【解决方案2】:

    听从别人的建议;基本上不做循环。执行操作查询(追加或更新)。

    这是“程序员”和“数据库开发人员”之间最常见的区别......

    【讨论】:

    • 我想我们都同意。但是,包含键列表的对象正在传递给例程。目前尚不清楚该列表的来源,但正如所写,所提供的数据不是表格格式,也不是来自我们知道的数据源。毫无疑问,如果该数据来自某个表源,则首选操作查询。 var “dict” 是一个对象,它甚至不知道该数据是否来自给定发布代码中的表。如果有办法获取对象集合并将其转换为查询,那么我会全力以赴。
    猜你喜欢
    • 2010-11-03
    • 1970-01-01
    • 2010-09-26
    • 2012-02-16
    • 2022-12-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    相关资源
    最近更新 更多