如何在用户定义的 VBA 函数中从另一个工作表中提取数据
我为一位同事创建了一个函数,其工作方式类似于 xLookUp,但允许用户从找到的最后一个匹配项中返回 N
。为了使该函数正常工作,我必须添加一个参数供用户输入 Worksheet.Name。如果没有这个参数,如果lookup_sheet不是输入函数的工作表,我就无法让函数从正确的工作表返回值。我了解 Application.Caller 方法可用于确保代码查看输入函数的工作表,但如何让它查看范围参数设置的工作表?
我的函数在名为“Supplies_List”的工作表中输入为 =xLookUp_X_From_Last (D2,"Orders",Orders!E:E,Orders!I:I,"",2)
,代码为:
Public Function xLookUp_X_From_Last(ByVal LookUp_Value As String, ByVal LookUp_Sheet As String, ByVal LookUp_Column As Range, ByVal Return_Column As Range, ifNA As String, Return_From_Last As Long) As String
Dim myCol As Collection
Dim i, LR, colCount, lColumn, rColumn, lookBack As Long
Dim lLetter, cLetter, s As String
Dim lSheet As Worksheet
Dim wb As Workbook
Set wb = ActiveWorkbook
Set lSheet = wb.Worksheets(LookUp_Sheet)
lookBack = Return_From_Last - 1
If LookUp_Column.Columns.Count <> 1 Or Return_Column.Columns.Count <> 1 Then
xLookUp_X_From_Last = "SELECTED RANGE ERROR"
Exit Function
End If
If LookUp_Value = "" Then
xLookUp_X_From_Last = ifNA
Exit Function
End If
Set myCol = New Collection
lColumn = LookUp_Column.Column
rColumn = Return_Column.Column
lLetter = Split(Cells(1, lColumn).Address, "$")(1)
LR = lSheet.Range(lLetter & Rows.Count).End(xlUp).Row
For i = 1 To LR
If lSheet.Cells(i, lColumn).Value = LookUp_Value Then
myCol.Add lSheet.Cells(i, rColumn).Value
End If
Next i
colCount = myCol.Count
If (colCount - lookBack) < 1 Then
s = ifNA
Else
s = myCol(colCount - (lookBack))
End If
xLookUp_X_From_Last = s
End Function
即使 LookUp_Column
参数引用带有 Orders!E:E
的工作表,我也必须添加LookUp_Sheet
参数。我很擅长子例程,但不太擅长函数,这让我很困惑。
我到处寻找vLookUp和xLookUp的VBA代码来剖析和学习,但没有找到。如果有人能给我指出这些函数的源代码,那就太棒了。
I created a function for a colleague which works like an xLookUp but allows the user to return the N
from last match found. To get the function to work I had to add an argument for the user to enter the Worksheet.Name. Without this argument, I could not get the function to return values from the proper sheet if the lookup_sheet was not the sheet the function was entered on. I understand the Application.Caller method can be used to make sure the code is looking at the sheet the function is entered on, but how do I have it look at the sheet the range argument is set to?
My function, entered on a worksheet called "Supplies_List" reads as =xLookUp_X_From_Last (D2,"Orders",Orders!E:E,Orders!I:I,"",2)
and the code is:
Public Function xLookUp_X_From_Last(ByVal LookUp_Value As String, ByVal LookUp_Sheet As String, ByVal LookUp_Column As Range, ByVal Return_Column As Range, ifNA As String, Return_From_Last As Long) As String
Dim myCol As Collection
Dim i, LR, colCount, lColumn, rColumn, lookBack As Long
Dim lLetter, cLetter, s As String
Dim lSheet As Worksheet
Dim wb As Workbook
Set wb = ActiveWorkbook
Set lSheet = wb.Worksheets(LookUp_Sheet)
lookBack = Return_From_Last - 1
If LookUp_Column.Columns.Count <> 1 Or Return_Column.Columns.Count <> 1 Then
xLookUp_X_From_Last = "SELECTED RANGE ERROR"
Exit Function
End If
If LookUp_Value = "" Then
xLookUp_X_From_Last = ifNA
Exit Function
End If
Set myCol = New Collection
lColumn = LookUp_Column.Column
rColumn = Return_Column.Column
lLetter = Split(Cells(1, lColumn).Address, "quot;)(1)
LR = lSheet.Range(lLetter & Rows.Count).End(xlUp).Row
For i = 1 To LR
If lSheet.Cells(i, lColumn).Value = LookUp_Value Then
myCol.Add lSheet.Cells(i, rColumn).Value
End If
Next i
colCount = myCol.Count
If (colCount - lookBack) < 1 Then
s = ifNA
Else
s = myCol(colCount - (lookBack))
End If
xLookUp_X_From_Last = s
End Function
Even though the LookUp_Column
argument references the sheet with Orders!E:E
, I had to add the LookUp_Sheet
argument. I'm pretty good at Subroutines, but not very good at functions and this has stumped me.
I searched high and low to find the VBA code for vLookUp and xLookUp to dissect and learn from, but could not find it. If anyone can point me to the source code for those functions that would be awesome as well.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
(a) 正如评论中提到的,您可以通过其
Parent
-Property获取某个范围的工作表
(b) 在 VBA 中几乎不需要处理列字符。
(c) 请小心变量声明。如果你想在一行中声明多个变量,你需要为每个变量指定类型,否则只有最后一个变量是你指定的类型,其他所有变量都声明为
Variant
。请参阅示例 https://stackoverflow.com/a/71250993/7599798(d) 子例程和函数之间几乎没有区别,只是函数返回一个值。如果您想将该函数用作 UDF,则有一些(明显的)限制,例如,不要修改底层 excel、不要使用
Select
和Activate
- 但是无论如何,你不应该在 VBA 中使用它。看看下面的函数。出于速度原因,我将查找和返回范围读入数组,特别是对于 UDF 速度很重要,这减少了 Excel 和 VBA 之间的往返次数。我还将最后 2 个参数设置为可选。
更新:您不需要Application.Caller。如果您将公式放入工作表中,就像
Excel 一样,会将前 3 个参数转换为
Range
变量,并且 Range 始终自动属于一个(且仅一个)工作表。第一个参数 (
D2
) 将指向公式所在同一工作表的单元格 D2,接下来的两个参数将指向工作表Orders
的 E 列和 I 列.不要对
Range.Address
不显示工作表名称的事实感到困惑 - Range 不是地址,它是指向一个或多个的对象工作表上的单元格。(a) As mentioned in the comments, you can get the worksheet of a range by it's
Parent
-Property(b) It is almost never neccessary to deal with column characters in VBA.
(c) Be careful with your variable declaration. If you want to declare multiple variables in one line, you need to specify the type for each of them, else only the last variable if of the type you specify, all the others are declared as
Variant
. See for example https://stackoverflow.com/a/71250993/7599798(d) There is nearly no difference between Subroutines and Functions, except that a function returns a value. If you want to use the function as UDF, you have some (obvious) limitations, eg don't modify the underlying excel, don't use
Select
andActivate
- but you shouldn't use that in VBA anyhow.Have a look to the function below. For speed reasons, I read the lookup and return range into an array, especially for UDFs speed matters and this reduces the amount of round-trips between Excel and VBA. I also made the last 2 parameters optional.
Update: You don't need Application.Caller. If you put a formula into your sheet, like
Excel will convert the first 3 parameters into
Range
-variables, and the Range always automatically belongs to one (and only one) Worksheet.The first parameter (
D2
) will point to the cell D2 of the same sheet where the formula lives, and the next two parameters will point to column E and I of sheetOrders
.Don't be confused by the fact that
Range.Address
doesn't show the sheet name - a Range is not an address, it's an object that points to one or more cell on a worksheet.