【问题标题】:Is there a way for VBA UDF to "know" what other functions will be run?VBA UDF 有没有办法“知道”将运行哪些其他功能?
【发布时间】:2013-02-10 14:50:38
【问题描述】:

假设我有一个将在工作表中使用超过 100,000 次的 UDF。在函数中,有没有办法让它知道在批处理中还要调用多少次?基本上我想做的是让每个函数都创建一个待办事项列表。我想做类似的事情:

IF remaining functions to be executed after this one = 0 then ...

有没有办法做到这一点?

背景:

我想创建一个 UDF,用户只需提供参数(日期、小时、节点、类型)即可执行 SQL 查询。如果您愿意在每次运行函数时实际执行 SQL 查询,这很容易做到。我知道这很容易,因为我这样做了而且速度非常慢。我的新想法是让函数首先查看它要查找的数据是否存在于全局缓存变量中,如果不存在则将其添加到全局变量“job-list”中。

我想要它做的是当调用最后一个函数时,然后遍历作业列表并执行最少数量的 SQL 查询并填充全局缓存变量。一旦缓存变量已满,它会刷新表以使所有其他函数再次被调用,因为在随后的调用中,它们会在缓存中找到所需的数据。

【问题讨论】:

  • 听起来这个控件无论如何都应该在函数之外。如果函数应该知道这一点,那将是一个糟糕的设计。该信息如何在 Batch 中可用?
  • 我很确定 UDF 无法访问此类信息。
  • @Bulat,我所说的批处理是指要运行的函数队列,就像用户将相同的函数粘贴到一堆单元格中一样。似乎查尔斯建议的应用程序事件可能会起到作用,只是不确定如何为我的目的实现它。我会玩弄它,看看我学到了什么。

标签: sql excel user-defined-functions excel-2010 vba


【解决方案1】:

首先:
VBA UDF 性能对 UDF 的编码方式极为敏感: 请参阅我关于编写高效 VBA UDF 的系列文章:

http://fastexcel.wordpress.com/2011/06/13/writing-efficient-vba-udfs-part-3-avoiding-the-vbe-refresh-bug/

http://fastexcel.wordpress.com/2011/05/25/writing-efficient-vba-udfs-part-1/

您还应该考虑使用数组 UDF 来返回多个结果:
http://fastexcel.wordpress.com/2011/06/20/writing-efiicient-vba-udfs-part5-udf-array-formulas-go-faster/

其次:
本系列的第 12 篇文章概述了使用 AfterCalculate 事件和缓存 http://fastexcel.wordpress.com/2012/12/05/writing-efficient-udfs-part-12-getting-used-range-fast-using-application-events-and-a-cache/
基本上,您需要的方法是让 UDF 检查缓存,如果不是当前的或可用的,则将请求添加到队列中。
然后使用计算后事件处理队列,必要时触发另一个重新计算。

【讨论】:

    【解决方案2】:

    从 Excel 电子表格执行 100,000 个 SQL 查询似乎是一个糟糕的设计。在这些之上创建缓存机制似乎使问题复杂化,使其比可能需要的更复杂。在某些情况下这可能是合适的,但我会考虑其他设计方法。

    最明显的是从 Excel 电子表格中获取数据并将其加载到数据库中的表格中。然后使用数据库对所有行进行一次处理。最后一步是将结果读回 Excel。

    我发现将大量行从 Excel 导入数据库的最佳方法是将 Excel 文件保存为 csv 并批量插入它们。

    这种方法可能不适用于您的问题。但总的来说,在数据库中运行的基于集合的方法会表现得更好。

    至于缓存机制,如果你必须走那条路。我可以想象一个具有以下伪代码的函数:

    Check if input values are in cache.
    If so, read values from cache.
    Else do complex processing.
    Load values in cache.
    

    这个逻辑可以放在函数中。不过,正如@Bulat 建议的那样,最好在函数周围添加一个额外的缓存层。

    【讨论】:

    • 我采用了需要 100,000 次 SQL 查询的方法,只是为了看看它的执行情况有多糟糕,而且速度慢得令人无法接受,正如预期的那样。您的伪代码是满足我需求的最明显的解决方案,但问题是“复杂处理”部分,它是我问题的核心。我的函数将做 3 件事中的 1 件事,要么从缓存中检索数据(简单),创建/附加到作业列表(简单),要么从作业列表创建缓存(可行),但困难的部分是让它在最后一个函数调用。 (即如果用户将 func 粘贴到 10k 个单元格中,它应该只创建一次缓存)
    • 完成“困难部分”的简单方法不在函数本身,而是在 AfterCalculate 事件中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 2015-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多