Excel 中用 VBA 字典查找代替 VLOOKUP

从上一篇《PYTHON操作EXCEL》可以看到,Python 操作 Excel 已非常自如方便。但是 Python 和相关库毕竟是一个额外的依赖,若能从 Excel 自身解决此类问题,自然是更为易用。

1. VBA 中的哈希表

用 Python 的着眼点主要是 VLOOKUP 公式太慢了,所以关键是要找到一种更高效的算法或数据结构定位数据。VLOOKUP 要求对列进行排序,内部应该是对列内数据进行二分查找,算法上不好再优化了,那就只好更换一种数据结构。搜索了一下,VBA 提供了 Scripting.Dictionary 这一词典结构,而且有文章说内部是哈希表实现,那就正是我要的东西了。

这样,VLOOKUP(lookup_value,table_array,col_index_num,range_lookup) 这一公式就转为下面的词典查找方式来实现:

  • 使用要从中进行查找的 table_array 内容构建词典。用 table_array 第一列作为 key,table_array 第 col_index_num 列作为 value,插入 Dictionary 中:Dictionary.Add key, value;
  • 查找时只需直接取 Dictionary 内的值 Dictionary.Item(lookup_value),即可完成查找;

若是仅仅 VLOOKUP 一次,倒也不必费劲先建立起一个词典。但当使用同样 VLOOKUP 公式的单元格很多时(比如几万个),就显得其必要了。因为 Dictionary 只需要建立一次,就可以用 O(1) 的复杂度进行多次查找了。

2. VLOOKUP 慢,主要问题不在算法上

从算法角度,词典查找的确快于二分查找,但优势并不是那么明显。所以在具体执行时,我发现使用词典查找的 VBA 宏运行速度并不比 VLOOKUP 快多少,运行时 Excel 仍然会导致系统假死几个小时。按说如此简单的程序不应该那么慢,问题究竟在哪里呢?

经过一段摸索,我才发现问题的根源所在:

  • VBA 往 Excel 表格中填内容时,会引发表格中已有公式的自动计算,非常耗时;
  • Excel 表格内容更新时,会触发屏幕显示内容的自动刷新,代价也很高;

所以提高 VBA 脚本执行性能的关键点,在于计算时关掉公式自动计算和屏幕刷新,这也是我始料未及的。在 VBA 中实现这两点很容易,但由于 VLOOKUP 本身即是公式,我没能想通直接调用 VLOOKUP 时如何避免这两点带来的性能损失。

3. 示例 VBA 代码

在做了上面提到的两次优化之后,原来 VLOOKUP N 个小时才能完成的任务,只用了 7 秒钟就执行结束了。

下面是我写的一段示例代码。我不熟悉 VBA 语言,只是照葫芦画瓢。代码规范程度相差甚远,但题意应是体现其中了。有心的朋友可以用作参考。

Sub 在机器表上生成一级分中心()
‘
‘ 在机器表上生成一级分中心 Macro
‘
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False

t0 = Timer
‘ 词典
Set map_dict = CreateObject("Scripting.Dictionary")

‘ 打开分中心映射表
Set map_sheet = Worksheets("分中心映射表")
map_nrows = map_sheet.Range("A300").End(xlUp).Row
Set my_rows = map_sheet.Range("A2:B" & map_nrows).Rows

‘ 遍历分中心映射表,获得 分中心 对应的一级分中心,插入词典
For Each my_row In my_rows
   center = my_row.Cells(1, 1).Value
   city = my_row.Cells(1, 2).Value
   If Not map_dict.Exists(center) Then
       map_dict.Add center, city
   End If
Next my_row

‘ 打开机器表
Set dispatch_sheet = Worksheets("机器表")
dispatch_nrows = dispatch_sheet.Range("a99999").End(xlUp).Row
Set my_rows = dispatch_sheet.Range("a1:b" & dispatch_nrows).Rows

‘ 遍历开通表,通过词典获得 machine_id 对应的一级分中心,插入开通表
For Each o_row In my_rows
   center = o_row.Cells(1, 2).Value
   o_row.Cells(1, 2).Value = map_dict.Item(center)
Next o_row

MsgBox "在机器表上生成一级分中心。共处理 " & dispatch_nrows & " 条记录,总耗时" & Timer - t0 & "秒。"

‘ 销毁建立的词典
Set map_dict = Nothing

‘ 打开自动计算和屏幕刷新
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
‘
End Sub

最后补充一点:我先实现的词典查找,后发现性能问题根源,所以未能去比较 VLOOKUP 与词典查找两种方式的具体性能差异。我想如果差异可以忍受,那么直接在 VBA 中调用 VLOOKUP 公式或许是一种更为简单的实现。

4. 测试用例

上面的代码如何测试呢?

id  name:分中心映射表,字典表    nickName id:机器表,待匹配表           匹配后结果:
1    a1                              b1    1                              b1    a1
2    a2                              b2    2                              b2    a2
3    a3                              b3    3                              b3    a3
4    a4                              b4    4                              b4    a4
5    a5                              b5    5                              b5    a5
6    a6                              b6    6                              b6    a6
7    a7                              b7    7                              b7    a7
8    a8                              b8    8                              b8    a8
9    a9                              b9    9                              b9    a9
10   a10                             b10   10                             b10   a10

— EOF —

Excel 中用 VBA 字典查找代替 VLOOKUP

时间: 2024-10-09 08:26:00

Excel 中用 VBA 字典查找代替 VLOOKUP的相关文章

Excel中用VBA实现删除空行和空列

Excel中用VBA实现删除空行和空列 在exce中删除空行和空列的方法有很多,相对而言删除空行较为简单,只需进行筛选,将空白行筛选出来,删除即可,但要删除空列比较困难.因为你不能按列进行筛选删除.Excel中没有这 个功能.当然你可以用另外一种方法,就是按列进行排序将空白列排序在一起,然后删除,但这种方法面临着列的顺序被打乱的问题,吃力不讨好.本文将利用Excel中强大VBA功能来完成删 除空白行和列的方法. 步骤一.打开excel,按ALT+F11组合建,调出VBA程序窗口 步骤二.在插入菜

【转】B树、B-树、B+树、B*树、红黑树、 二叉排序树、trie树Double Array 字典查找树简介

B  树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中:否则,如果查询关键字比结点关键字小,就进入左儿子:如果比结点关键字大,就进入右儿子:如果左儿子或右儿子的指针为空,则报告找不到相应的关键字: 如果B树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树的搜索性

excle查找操作-vlookup的使用心得

百度了一下vlookup的语法规则: 该函数的语法规则如下: VLOOKUP(lookup_value,table_array,col_index_num,range_lookup) 参数 简单说明 输入数据类型 lookup_value 要查找的值 数值.引用或文本字符串 table_array 要查找的区域 数据表区域 col_index_num 返回数据在区域的第几列数 正整数 range_lookup 模糊匹配 TRUE(或不填) /FALSE range_lookup最好填入false

EXCEL的VBA开发笔记

最近需要帮忙用EXCEL记一些账目,为了提高效率,稍微学习下了VBA进行编程,笔记之. 在EXCEL中按alt+F11就能跳到VB的开发界面 变量定义: Dim        变量as 类型   '定义为局部变量,如 Dim          xyz as integerPrivate    变量as 类型   '定义为私有变量,如 Private       xyz as bytePublic     变量as 类型   '定义为公有变量,如 Public        xyz as sing

excel中vba将excel中数字和图表输出到word中

参考:https://wenku.baidu.com/view/6c60420ecc175527072208af.html 比如将选区变为图片保存到桌面: 1 Sub 将选区转为图片存到桌面() 2 Dim ans As Byte, Pic As String, Paths As String 3 On Error Resume Next 4 Paths = CreateObject("WScript.Shell").SpecialFolders("Desktop"

字典查找、linq、foreach、yield等几种查找性能对比

先上代码,以1千万记录的内存查找测试: List<Student> stuList = new List<Student>(); Dictionary<int, Student> dictStu = new Dictionary<int, Student>(); Student student = null; for (int i = 0; i < 10000000; i++) { student = new Student(i); stuList.A

Excel 2010 VBA实战技巧精粹——互动出版网

这篇是计算机类的优质预售推荐>>>><Excel 2010 VBA实战技巧精粹> 经典图书<别怕,Excel VBA其实很简单>进阶篇,是VBA学习宝典!Excel Home精锐团队倾力打造.提高效率.化繁为简从本书开始 内容简介 <Excel 2010 VBA实战技巧精粹>内容侧重于Excel VBA使用技巧,旨在帮助Excel VBA的初学者和有一定Excel VBA应用基础.希望进阶的读者.全书精选了251个技巧,辅以深入浅出的剖析,力求让

Excel中用VLOOKUP实现join

最近处理数据时遇到需要将Excel中两个表数据按指定列作为条件进行连接合并的需求,若使用程序来实现稍微有点麻烦, 想到excel有内置函数,去网上查了下,发现可以很方便的处理这种需求. 先说下原始的需求: 现在有两个表: sheet1:   A B 1 userid level 2 1001 12 3 1002 15 sheet2:   A B 1 userid username 2 1001 test1 3 1002 test2 希望合并后新得到的sheet1:   A B C 1 useri

在excel中用VLOOKUP函数应注意的一个问题

作者:iamlaosong 同事在用VLOOKUP函数时碰到一个问题,就是明明两个字符串相同,但查找的结果却是#N/A,比较的字符串都是数字号码,经检查,发现两个表中相应字符串的类型不同. excel单元格中的值全为数字字符时,属性可以是数值,也可以是文本,而用vlookup函数查找时,关键字段两边数值类型必须要一致,否则是找不到的,同事的表格中原表中的号码是文本型,需要比较的号码是数值型,导致无法找到,转换成一致问题就解决了,用文本型和数值型均可. 批量转换时会发现传统的方法无效(选中列,改变