VBA Excel - 大型Scripting.Dictionary的性能问题

时间:2017-05-23 14:18:02

标签: excel vba excel-vba

我构建了一个查询内存中字典的函数,以避免使用另一个查询服务器上实际数据库的函数。目标是只查询数据库一次(因为查询有点慢)并将结果保存在内存中以便调用。我在电子表格中有大约25k的单元格,带有原始的数据库查询功能,我希望用下面的函数替换它。

这导致了一个非常大的公共全局字典(大约25k字符串键,每个键长约75个字符),但我发现当我运行此函数时,性能比仅查询实际数据库更糟糕服务器。我希望有人可以提出一些方法来使这个表现更好......

Public dataDict As New Scripting.Dictionary

Public Function DB_Wrap(database As String, query As String) As Double
    Dim q As ServerDatabase
    Dim key As String
    Dim result As Double

    key = database & "|||" & query

    If dataDict.Exists(key) = False Then
        Set root = New ServerDatabase
        Set q = root.Open(database)

        result = q.total(query)
        dataDict.Add key, result
    Else
        result = dataDict(key)   'its when I run this that the performance isn't great. 
    End If

    DB_Wrap= result
End Function

2 个答案:

答案 0 :(得分:2)

这并不是真正的答案,但由于太长而无法发表评论。

我有一个过去可以正常工作的宏,但是由于我们切换到O365似乎很慢。我知道关联并不总是暗示因果关系。 将项添加到字典中并测试重复项是可以的,大约需要30秒才能添加88k键,但是随后将所有唯一对写入新表需要花费很长时间。

i = 0
T = Timer()
For Each K In L
    DoEvents
    Set D = D.Offset(1, 0)
    i = i + 1: If i Mod 1000 = 0 Then Debug.Print Timer() - T
Next

给出0.20、0.41、0.61 ...并在18秒内完成。

如果我们实际上访问K的值:

For Each K In L
    DoEvents
    D.Value = K
    Set D = D.Offset(1, 0)
    i = i + 1: If i Mod 1000 = 0 Then Debug.Print Timer() - T
Next

然后进度逐渐变慢,我太急了,不能完成它: 104、376、810、1424秒,因此要完成15个 小时 ,并建议您每秒钟访问0.6秒。

如果我们使用键搜索值:

For Each K In L
    DoEvents
    D.Value = K
    D.Offset(0, 1).Value = L(K)
    Set D = D.Offset(1, 0)
    i = i + 1: If i Mod 1000 = 0 Then Debug.Print Timer() - T
Next

每1000个条目的时间戳分别变为121、417、909、1561,这表明,令人惊讶的是,它没有找到花费时间的键的值,而是在这种情况下甚至通过迭代来访问键本身。

我还尝试通过索引访问键和值,但这并不好:

For i = 0 To L.Count
    DoEvents
    D.Value = L.Keys(i)
    D.Offset(0, 1).Value = L.Items(i)
    Set D = D.Offset(1, 0)
    If i Mod 1000 = 0 Then Debug.Print Timer() - T
Next

基本上,我将不得不放弃使用字典来检测和删除重复条目的想法(这是我使用它的唯一原因)

答案 1 :(得分:1)

要尝试的一些事项:

您正在通过key = database & "|||" & query每次调用创建一个新字符串,并且您每次都要传递'数据库'和'查询'的开销(虽然它们是通过引用传递的,但仍然会增加)而且你每次都为字符串分配内存。有没有什么方法可以将这些存储在一个实例中,而不是每次调用函数时都创建它们?也许您可以同时创建所有必需的字符串并传递它们,而不是每次都在函数中创建它们?

我认为Set q = root.Open(database)在这里是缓慢的部分所以如果你能在一个实例中得到所有的密钥它应该有所帮助。

你可以缩短按键吗?处理的字符越多,它就越慢。

而不是声明dataDict As New Scripting.Dictionary在您的活动代码中声明它:

Dim dataDict As Scripting.Dictionary

Set dataDict = New Scripting.Dictionary

如果您使用New关键字声明,您的代码通常会在执行订单之前检查对象是否存在。

最后,数据如何从服务器进入字典?也许阵列会更好?