Access中没有max(x,y)函数

16

Access的VBA缺少一个简单的Max(x,y)函数来查找两个或更多值的数学最大值。我习惯于在其他语言(如perl/php/ruby/python等)的基本API中已经有这样的函数。

我知道可以这样做:IIf(x > y, x,y)。还有其他可用的解决方案吗?


您可以在Access中使用Excel函数。(更多详情请见。) - ashleedawg
8个回答

10

我理解这个问题的意思是:

如何在Access中实现一个函数,返回一组数字的最大/最小值?以下是我使用的代码(类比于IIf,命名为“iMax”,即“立即 If” / “立即 Max”):

  Public Function iMax(ParamArray p()) As Variant
  ' Idea from Trevor Best in Usenet MessageID rib5dv45ko62adf2v0d1cot4kiu5t8mbdp@4ax.com
    Dim i As Long
    Dim v As Variant

    v = p(LBound(p))
    For i = LBound(p) + 1 To UBound(p)
      If v < p(i) Then
         v = p(i)
      End If
    Next
    iMax = v
  End Function

  Public Function iMin(ParamArray p()) As Variant
  ' Idea from Trevor Best in Usenet MessageID rib5dv45ko62adf2v0d1cot4kiu5t8mbdp@4ax.com
    Dim i As Long
    Dim v As Variant

    v = p(LBound(p))
    For i = LBound(p) + 1 To UBound(p)
      If v > p(i) Then
         v = p(i)
      End If
    Next
    iMin = v
  End Function

至于为什么Access不会实现它,我认为这不是一个非常普遍的需求。它也不是非常像数据库的东西。您已经拥有了在域和行集中查找最大/最小值所需的所有功能。它也不是很难实现,或者当您需要时只需编写一次比较即可。

也许以上内容可以帮助某些人。


3
这比在问题发布后一年的踩票更有用。 :) - DGM
2
我很惊讶这个问题没有更早得到答案,因为问题非常简单。也许是因为这个问题有点挑衅(即诋毁Access),所以那些知道答案的人被激怒了,不愿意帮助一个似乎有点偏见的人。 - David-W-Fenton
1
你明白为什么数据库开发工具不会内置这样的功能吗?与此类似的函数相比,数据库相关的遗漏要大得多。 - David-W-Fenton
有没有人能指向一个更常见的解决方案,它不仅接受数字数组作为函数参数,还接受数组和集合? - Argut

4

从MS Access VBA调用Excel VBA函数

如果您添加对Excel的引用工具引用Microsoft Excel x.xx对象库),则可以使用WorksheetFunction调用大多数Excel工作表函数,包括MAX(也可用于数组)。

示例:

MsgBox WorksheetFunction.Max(42, 1999, 888)

或者,
Dim arr(1 To 3) As Long
arr(1) = 42
arr(2) = 1999
arr(3) = 888
MsgBox WorksheetFunction.Max(arr)

第一次调用需要1秒钟才能响应(实际上对我来说是1.1秒),但随后的调用时间更加合理(对我来说每个调用<0.002秒)。


将Excel作为对象引用

如果您在过程中使用了大量的Excel函数,您可以通过使用一个Application对象直接引用Excel来进一步提高性能。

例如,此过程迭代一组记录,重复使用Excel的MAX函数对字节数组进行操作,以确定每个记录的“最高”ASCII字符。

Option Compare Text
Option Explicit
'requires reference to "Microsoft Excel x.xx Object Library"
Public excel As New excel.Application 

Sub demo_ListMaxChars()
    'list the character with the highest ASCII code for each of the first 100 records
    Dim rs As Recordset, mx
    Set rs = CurrentDb.OpenRecordset("select myField from tblMyTable")
    With rs
        .MoveFirst
            Do
                mx = maxChar(!myField)
                Debug.Print !myField, mx & "(" & ChrW(mx) & ")"  '(Hit CTRL+G to view)
                .MoveNext
            Loop Until .EOF
        .Close
    End With
    Set rs = Nothing     'always clean up your objects when finished with them!
    Set excel = Nothing  
End Sub

Function maxChar(st As String)
    Dim b() As Byte                             'declare Byte Array
    ReDim b(1 To Len(st))                       'resize Byte Array
    b = StrConv(st, vbFromUnicode)              'convert String to Bytes
    maxChar = excel.WorksheetFunction.Max(b)    'find maximum Byte (with Excel function)
End Function

1

两个函数都存在空值问题,我认为这样会更好。

Public Function iMin(ParamArray p()) As Variant
  Dim vVal As Variant, vMinVal As Variant

  vMinVal = Null
  For Each vVal In p
    If Not IsNull(vVal) And (IsNull(vMinVal) Or (vVal < vMinVal)) Then _
      vMinVal = vVal
  Next

  iMin = vMinVal
End Function

感谢您的贡献。空值确实会出现,有人能够处理它们是很好的。 - rohrl77

1
我喜欢DGM对IIF语句的运用和David对For/Next循环的运用,因此我将它们结合起来使用。

由于access中的VBA没有严格的类型检查,我将使用变体来保留所有数字,包括整数和小数,并重新定义返回值的类型。

感谢HansUP注意到我的参数验证 :)
添加注释以使代码更加友好。
Option Compare Database
Option Base 0
Option Explicit

Function f_var_Min(ParamArray NumericItems()) As Variant
If UBound(NumericItems) = -1 Then Exit Function ' No parameters
Dim vVal As Variant, vNumeric As Variant
vVal = NumericItems(0)
For Each vNumeric In NumericItems
    vVal = IIf(vNumeric < vVal, vNumeric, vVal) ' Keep smaller of 2 values
Next
f_var_Min = vVal ' Return final value
End Function

Function f_var_Max(ParamArray NumericItems()) As Variant
If UBound(NumericItems) = -1 Then Exit Function ' No parameters
Dim vVal As Variant, vNumeric As Variant
vVal = NumericItems(0)
For Each vNumeric In NumericItems
    vVal = IIf(vNumeric < vVal, vVal, vNumeric) ' Keep larger of 2 values
Next
f_var_Max = vVal ' Return final value
End Function

这两个函数唯一的区别在于IIF语句中vVal和vNumeric的顺序不同。
对于每个子句使用内部VBA逻辑处理循环和数组边界检查,而“Base 0”将数组索引从0开始。

1
IsNull(NumericItems)永远不会为True。 如果你的意图是在没有参数调用时退出该函数,请尝试检查UBound(NumericItems) = -1 - HansUp
好眼力!用了您的建议在一些样本数据上,效果和广告一样好,继续保持! - CoveGeek

1

他们可能认为你会使用DMAX和DMIN或者SQL MAX,只能在Access数据库中工作?

我也很好奇为什么这样做...似乎在创建临时表并将表单值添加到表中,然后在表上运行DMAX或MAX查询以获取结果是一个过度的方式...


是的,我使用的值在数据库中,但在同一行的两个列中。这不仅仅是聚合Max,而是简单的数学Max。 - DGM

1

我曾经创建过一个小的projMax()函数来处理这些问题。虽然VBA可能永远不会得到增强,但以防万一他们添加了适当的Max(和Min)函数,它也不会与我的函数冲突。顺便说一句,原帖建议使用IIF...那样做是可以的,但在我的函数中,我通常会加入几个Nz()来防止null破坏函数。


0

你可以在Access VBA中调用Excel函数:

Global gObjExcel As Excel.Application

Public Sub initXL()
    Set gObjExcel = New Excel.Application
End Sub

Public Sub killXL()
    gObjExcel.Quit
    Set gObjExcel = Nothing
End Sub

Public Function xlMax(a As Double, b As Double) As Double
    xlCeiling = gObjExcel.Application.Max(a, b)
End Function

-1

在Access VBA中,您可以使用Worksheetfunction.max()worksheetfunction.min()。希望这能帮到您。


1
稍微多提供一些信息会更有帮助,因为这不能直接使用。(我在下面进行了扩展。) - ashleedawg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接