如两个图像所示,有两个工作表。“结果”是我希望结果所在的工作表,“从”工作表是要从中搜索的源。基本上,我希望根据“班级编号”和“学生编号”搜索该学生的姓名。“班级编号”和“学生编号”都不是唯一的,这意味着可能存在重复。但是,“class number”和“student number”的组合是唯一的,这意味着每个学生都有不同的“class number”和“student number”组合。因此,我想到的方法是首先创建一个支持列,将“class number”和“student number”连接起来,然后执行VlookUp。代码如下所示:
Sub vlookupName()
'get the last row of both sheets
resultRow = Sheets("Result").[a1].CurrentRegion.Rows.Count
fromRow = Sheets("From").[a1].CurrentRegion.Rows.Count
'concat Class number and student number to get a unique string used for vlookup
Sheets("Result").Range("D2:D" & resultRow) = "=B2 & C2"
Sheets("From").Columns("A").Insert
Sheets("From").Range("A2:A" & resultRow) = "=c2 & d2"
'vlookup
Sheets("Result").Range("A2:A" & resultRow) = Application.VLookup(Sheets("Result").Range("D2:D" & resultRow).Value, _
Sheets("From").Range("a2:b" & fromRow).Value, 2, False)
'(delete columns to get back to raw file for next test)
Sheets("Result").Columns("D").Delete
Sheets("From").Columns("A").Delete
Sheets("Result").Range("A2:A" & resultRow) = ""
End Sub
对代码或方法的任何部分的改进都是可以理解的。
8条答案
按热度按时间3mpgtkmj1#
尝试使用多个值进行查找时,连接是危险的。请考虑以下两种情况:
| 分类编号|学员编号|
| - -|- -|
| 一个|十五|
| 十一|五个|
两个连接都将产生115,这不是唯一的。
你可能会说,添加一个分隔符可以解决这个问题。比如下划线,上面的两个例子将变成1_15和11_5。是的,只要你的部分是数字,这就可以了,但是如果它们是文本呢?比如:
| 第一部分|第二部分|
| - -|- -|
| 1个_|五个|
| 一个|第5页|
两个连接都将产生1__5,这也不是唯一的。虽然最后一个例子是强制的,我希望它能证明这一点,即这种方法是不干净的,可能会导致错误的结果。
根据您的2张图片中显示的范围,我将在Result工作表的单元格A2中编写以下公式:
=INDEX(From!$A$2:$A$11,MATCH(1,INDEX((From!$B$2:$B$11=$B2)*(From!$C$2:$C$11=$C2),0),0))
或者用更英语的方式说:
=INDEX(ResultRange,MATCH(1,INDEX((KeyPart1Range=DesiredPart1)*(KeyPart2Range=DesiredPart2),0),0))
,其可以通过添加part 3、part 4等来容易地扩展,以匹配所需的尽可能多的标准。逻辑很简单:
From!$B$2:$B$11=$B2
之类的函数将返回一个布尔值数组(TRUE
和FALSE
),该数组对应于From!$B$2:$B$11
范围中的行数1.将两个(或更多)布尔数组相乘将得到一个1和0的数组,其中1表示
TRUE
,0表示FALSE
INDEX(array,0)
将返回完整数组,而无需按Ctrl+Shift+Enter(非Office 365的Excel版本需要)MATCH(1,...)
将返回满足所有指定条件的行索引1.最外层的
INDEX
返回所需的结果为什么要运行VBA代码来重新创建一个可以直接在Excel中完成的公式?这通常“闻起来”是不好的做法。这种方法使整个项目的可维护性变得更加困难。如果重命名工作表,则需要更新代码。如果更改范围(例如插入一列),则需要更新代码。如此等等。
假设您不希望在最终结果选项卡中包含公式,那么为什么不创建一个包含所有公式(Excel公式)的中间工作表,然后您的代码可以简单地复制粘贴到最终结果选项卡中,那里将只有值。这样,如果您需要添加额外的逻辑,您可以在普通Excel中只处理中间工作表,而不必担心同步任何代码。
iqxoj9l92#
这不是VBA的答案,但值得注意的是,这里可以使用MATCH()的“多列”版本:
lndjwyie3#
您可以使用新的
FILTER
-函数来检索指定班级的学生姓名和学生编号。由于组合是唯一的,因此公式将仅返回一个值。在我的例子中,我假设你使用表格来表示from-data和result-data(Insert〉Table)。我更喜欢这种方法,因为你可以在公式中使用可读的名称。
=FILTER( tblData[Name], (tblData[Class Number]=[@[Class Number]])*(tblData[Student Number]=[@[Student Number]]), "[???]")
将返回如果要保留VBA解决方案:
rta7y2nd4#
如果您处理MS Excel 365,只需在工作表
"Result"
的单元格A2
中输入以下公式,该公式将所有找到的名称动态显示为溢出范围;* 当然,您可以根据需要调整引用单元格范围 *。LET()
函数说明LET
使您能够Names
,b)从Result
工作表构建的SrchId
,c)基于工作表From
的DataId
)和作为
LET
函数最后一个参数的计算部分现在简单如下:优点:
公式体系结构
这些点在一定程度上可以满足赏金猎人对“优雅、有效、洞察力”的要求。
n53p2ov05#
您不需要为此使用VBA,但有几个公式就足够了。
为了让索引在没有找到匹配时也能正常工作,你需要避免
0
作为任何计算的结果。一种方法是在没有找到匹配时创建一个错误,然后使用IFERROR(value, value_if_error)
捕获它。类似地,当使用MATCH
时,不匹配也会给予一个要捕获的错误。因此,使用连接符号来避免溢出,您可以得到如下结果:
=IFERROR(INDEX(A$1:A$13,MATCH(I$1&"#"&J$1,B$1:B$13&"#"&C$1:C$13,0)),"")
如果需要,您也可以使用SUMPRODUCT(尽管查找数据必须具有唯一性,否则它将对多行求和,并从
INDEX
给予错误的结果):=IFERROR(INDEX(A$1:A$13,SUMPRODUCT((B$1:B$13=I1)*(C$1:C$13=J1)*(ROW(B$1:B$13)))-ROW(B$1)+1),"")
然后,如果要引用不同的工作表,则需要使用相应的工作表引用来限定每个区域,例如:
=IFERROR(INDEX(From!$A$2:$A$12,SUMPRODUCT((From!$B$2:$B$12=B2)*(From!$C$2:$C$12=C2)*(ROW(From!$B$2:$B$12)))-ROW(From!$B$2)+1),"")
piv4azn76#
您可以尝试在工作表中使用自定义搜索功能。然后将其用作另一个内置功能
位于Excel工作表中VBA Module1的函数代码
有关自定义函数的详细信息,请参阅HERE
scyqe7ek7#
如果您的数据集很小,您可以使用数组并通过UDF调用它。
我的结果表:
我的自定义项编码:
我添加了一个“未找到”选项,以防没有匹配项。
这种方法的优点是,在连接学生号和班级号时,不必担心出现重复项。
但请注意,如果数据集很小,这将正常工作。如果它太大,它可能会导致性能问题。
**EDIT:**我在单元格B2中键入
=GET_NAME(From!$A$2:$C$8;Result!C2;Result!B2)
并向下拖动来调用此函数。xjreopfe8#
替代
FilterXML
方法我将通过
FilterXML()
函数演示一种替代方法,只需要三个步骤:getRange()
*系统提示
FilterXML()函数(自2013及以后版本起可用)需要以下参数:
2)
-XPath表达式,在此定义在任何层级//i
处的搜索节点(即<i>..</i>
),以及在括号[..]
中的并列"And"
条件,其定义紧接着的相邻节点的期望值内容。会产生类似
<r><i>Amy</i><i>1</i><i>22</i><i>Richard</i><i>1</i><i>17</i>...</r>
的字串,其中自由选择的名称<r>
代表文件元素,例如root,<i>
代表item。用户定义函数
GetStudentName()
示例帮助函数
wellformed()
下面的帮助函数使用自2019+版本起可用的►
TextJoin()
函数构建了一个所谓的“格式良好”的xml字符串。-然而,基于给定范围的数据字段数组中所有值的循环,重写此函数是很容易的。帮助函数
check()
提供可能的异常(即1个或多个发现),因为OP仅等待唯一的发现。* 请注意,后期绑定
Application.FilterXML
允许分析这些异常,而无需On Error
处理。*帮助函数
GetRange()
仅返回引用工作表的完整数据区域(此处为:
"From"
)。* 此外,该函数还允许定义可选的列边界,这些边界也可用于其他项目。*