重新计算自定义 VBA 函数,仅应请求

Posted

技术标签:

【中文标题】重新计算自定义 VBA 函数,仅应请求【英文标题】:Recalculate Custom VBA functions, Only on Request 【发布时间】:2013-03-25 03:37:08 【问题描述】:

我的问题是这样的:

我想使用自定义功能区命令按钮,甚至电子表格中的简单命令按钮来初始化 OLEDB 数据库连接,并更新/重新计算所有需要此类连接的相关用户定义函数,或我指定的函数。除了单击特定按钮时,我不希望重新计算这些函数中的任何一个。我很难弄清楚如何做到这一点。请提供您的帮助或建议。

有关我所做的详细信息,请参见下文:

我目前将数据存储在一个访问数据库中,我在 excel 中使用 vba 来进行特定查询。我已将每个 datarequest 例程嵌入到名为 [fnc] 的模块下的一组函数中。然后,我从 Excel 电子表格中将它们作为用户定义的函数进行访问。这里给出一个例子:

 Function ValueV(mm As String, yy As String, qtable As String, qcode As String, compare_period As Integer, average_period As Integer, weight As Boolean) As Variant
 'Month Value Formula for Horizontal Data
 'mm - month value 2-digit
 'yy - year value 4-digit
 'qtable - query table name eg. "cpia"
 'qcode - query code for variable eg. "all0100"
 'avgperiod - lag periods to average in calculation eg. 3-avgperiods for quarterly measure, 1-avgperiod for point measure.
 'weight - boolean (true or false) value for weighting values given reference weight. Currently unsupported. Code should be extended to include this feature. (space holder for now)
  Dim lag_value As Variant
  Dim cur_value As Variant
  lag_value = 0
  cur_value = 0


     'STEP-A: Gets the initial Value average or not.
     '===============================================================
     If compare_period > 0 Then
     'Use this step to pickup initial value when compare_period <> 0 which requires a % change as opposed to a point value.
     'Average_period must be greater than or equal to one (1). One (1) represents the current month which is the same as a point value.
         lmm = fnc.lagdate(mm, yy, compare_period, "mm")                          'lag month (a single month for mValueH)
         lyy = fnc.lagdate(mm, yy, compare_period, "yy")                          'lag year (a single month for mValueH)
         smm = fnc.lagdate(mm, yy, compare_period + average_period - 1, "mm")     'dating backwards to account for average period
         syy = fnc.lagdate(mm, yy, compare_period + average_period - 1, "yy")     'dating backwards to account for average period
         'note, for smm & syy, the average period includes the lmm so we add back one (1)
         'eg. 3-mth average is not 3-lags but current and 2-lags.
         sdate1 = syy & fnc.numtext(smm)
         'start date for query (begining of lag value including average period)

         Set MyRecordset = New ADODB.Recordset
         mysql = sql.sqlVSers(lmm, lyy, qtable, qcode, sdate1)
         'MsgBox (MySql)
         MyRecordset.Open MySql, MyConnect, adOpenStatic, adLockReadOnly

         Do Until MyRecordset.EOF     'Loop to end and enter required values
         lag_value = lag_value + MyRecordset(qcode)
         MyRecordset.MoveNext
         Loop
         'Stop
         lag_value = lag_value / average_period
         MyRecordset.Close
     End If


     'STEP-B: Gets the current Value average or not.
     '===============================================================
         smm = fnc.lagdate(mm, yy, average_period - 1, "mm")           'dating backwards to account for average period
         syy = fnc.lagdate(mm, yy, average_period - 1, "yy")           'dating backwards to account for average period
         sdate1 = syy & fnc.numtext(smm)
         'start date for query (begining of lag value including average period)

         Set MyRecordset = New ADODB.Recordset
         MySql = sql.sqlVSers(mm, yy, qtable, qcode, sdate1)
         MyRecordset.Open MySql, MyConnect, adOpenStatic, adLockReadOnly

         Do Until MyRecordset.EOF     'Loop to end and enter required values
         cur_value = cur_value + MyRecordset(qcode)
         MyRecordset.MoveNext
         Loop

         cur_value = cur_value / average_period
         MyRecordset.Close


     'STEP-C: Calculates the Requested % Change or Point Value.
     '===============================================================
     If compare_period = 0 Then
         ValueV = cur_value
     Else
         ValueV = cur_value / lag_value * 100 - 100
     End If

 End Function

由于我完全绕过了子例程的使用,与数据库的连接目前是作为工作簿帮助例程完成的,如下所示。

Private Sub Workbook_AfterSave(ByVal Success As Boolean)
Dim filePath
filePath = ThisWorkbook.Path
If Right$(filePath, 1) <> "\" Then filePath = filePath & "\"
MyConnect = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & filePath & "rsdata.accdb;"
End Sub

问题是,这个更新过程不太理想。理想情况下,我想在菜单栏中放置一个自定义按钮(单击它时)将连接到数据库并重新计算给定工作表或工作簿中使用的所有用户定义函数。

请提供您的建议或指出以前可能做过类似事情的地方。

提前致谢。 JR。

【问题讨论】:

“控制”的一种方法是定义一个全局变量(例如 Recalc = True/False)或命名单元格。通过用户交互设置变量。虽然很难控制何时将变量设置回 False(不能成为 UDF 的一部分,除非您计算每个具有 UDF 的单元格都已重新计算) 【参考方案1】:

您正试图将UDF 用于他们不打算做的事情。它们设计的目的是像其他单元格公式一样运行,并在 Excel 决定它们需要时进行计算。

你有两个选择

重新设计您的应用程序以不使用 UDF(IMO 最佳方式) 修改您的 UDFs 以仅响应您指定的触发器,例如按钮单击(IMO 是一个杂乱无章的问题,通常是个坏主意)

如何重新设计以避免UDF's 取决于您的 OP 中未披露的因素

【讨论】:

以上是关于重新计算自定义 VBA 函数,仅应请求的主要内容,如果未能解决你的问题,请参考以下文章

创建永不重建的自定义钩子

使用VBA代码实现简单自定义函数

从自定义 VBA 函数(非子函数)写入 Excel 工作表

如何从 VBA 函数返回自定义类型

VBA-自定义函数和带参数的过程

定义返回空白的自定义 VBA 函数