1.       问题由来

早晨还没有完全醒来,你就被电话吵醒,有一个中学同学向你请教一个Excel的问题。作为一个所谓的Excel专家,你经常会受到此类骚扰。问题大概是这样的,一个很大的Excel文件,其中有些行是重复的,也就是说,有2行是完全一样的,而有些行是不重复的,现在的问题是要找出所有不重复或者重复的行,你没有听明白。你大概考虑了一下,用“VLOOKUP”查找一下,然后重新排序,应该就可以了,你需要试一下,然后告诉他怎么用,于是你告诉他,20分钟后再打电话给你。

2.       问题解决的思路

你首先打开Excel,输入一些测试数据,大概是这个样子:

其中“张三”、“李四”有2个,其他只有一个,需要把他们分出来。首先在B列输入1,然后向下填充,在C列输入“VLOOKUP(A1,$A$1:$B$7,2,FALSE)”[1],如果找到,那么返回1,如果找不到,空着就可以了。结果C列全部变成了1 ,因为查找自己肯定可以找到,那么查找的Range必须要去除本行。

你接着找了几个其他函数,“MATCH”,“INDEX”试了试,都无法办到;那么用IF函数呢,你开始试着写IF函数。先输入第4行吧,参数和引用区域回头再处理,或许Excel聪明到可以填充出你需要的引用区域。

你输入了如下的IF函数:

IF(OR(VLOOKUP(A4,A1:B3,2,FALSE),VLOOKUP(A4,A5:B7,2,FALSE)),1,0)

真够复杂的,Excel应该开一个小窗口,然后作为代码输入这样的判断逻辑,IF函数可以嵌套7层,真不知道微软的工程师怎么想的[2],你一边嘟囔一边按下了回车,结果是“#N/A”,就是“值不可用”,你知道函数 VLOOKUP如果找不到需要的值,则返回错误值 #N/A,表达式里有了这个东东,所以不管什么计算,结果都是它了。

从工具菜单选择“错误检查”,“显示计算步骤”,证实了你的猜测,第二个VLOOKUP函数返回的错误值 #N/A传递到了最后。

这时,你同学的电话来了,你告诉他需要写一段小程序,你决定还是使用直接又简单的VBA来解决问题。

3.       VBA程序

打开VBA编辑器,插入一个模块,你不假思索的敲入了以下代码:

Sub SelectDouble()

Dim i As Long, j As Long

For i = 1 To 7 Step 1

For j = 1 To 7 Step 1

'不比较相同的行

If i <> j Then

If Range("A" & i).Value = Range("A" & j).Value Then

Range("E" & i).Value = 1

End If

End If

Next j

Next i

End Sub

点击运行,很好,是重复的都标志了1,没有重复的空着,然后排序就可以了。你很满意你还输入了一行注释。你拨通了你同学的电话,告诉他可以了,然后他打电话给你,你把程序念给他,告诉他该改什么地方。天知道他上学时学的什么语言,反正不是Basic,你得解释Dim是什么含义。经过一番折腾,他终于在电话另一端把代码输入了计算机。作为电信员工的他可以每天24小时用电话聊天,只是可怜你的手机话费单,你叹了口气,该去洗脸刷牙了。

4.       效率

洗完脸,刷完牙,你泡好了一杯咖啡,又回到了计算机旁边,电话又来了。你以为是告诉你已经完成了的“喜讯”,听到的却是说死机了,愣了0.1秒钟,你想想应该是程序还在执行或者是死循环。你问了他大概的数据量,知道大概有9000多条记录,还好,你想。

你检查了一下代码,没有什么死循环,也许是你同学输入时有什么错误,你把循环改到1到10000,然后拿起杯子,咽了一口咖啡,往后靠了靠,等着计算结果。几分钟过去了,还是没有结束,你觉得有些奇怪,你敲了“Ctrl + Break”,暂停了程序,将鼠标放在i变量上,显示i还是24,TNND,你知道是Range函数太慢,算了,你打电话告诉你同学,大概需要几个小时才可以计算完成。你又喝了一口咖啡,自言自语道,比起手工筛选,毕竟很快了。

但不就不到1万条纪录吗,Excel的VLOOKUP等内置函数一眨眼也就计算好了啊。

4.1.      通过数组

数组要比Range函数快一些,你把程序改了一下,定义了2个数组,首先把数据全部读入第一个数组,然后对数组进行操作,对于重复的,把第二个数组的相应部分写为1,计算完成后,根据第二个数组,把结果写回去。程序代码如下:

Sub SelectDouble2()

Dim i As Long, j As Long

Dim max As Long

Dim a() As String, b() As Long

max = 10000

ReDim a(max) As String

ReDim b(max) As Long

For i = 1 To max Step 1

a(i) = Range("A" & i).Value

Next i

For i = 1 To max Step 1

For j = 1 To max Step 1

'不比较相同的行

If i <> j Then

If a(i) = a(j) Then

b(i) = 1

End If

End If

Next j

Next i

For i = 1 To max Step 1

Range("F" & i).Value = b(i)

Next

End Sub

你执行了一下,对于10000条纪录,大概需要不到5分钟。你觉得很满意,效率提高了几个数量级,你还没有忘记设置了一个max变量,这样,代码使用时改动就会少很多。

4.2.      使用内置函数

你又想起了VLOOKUP这个函数,真是阴魂不散。是啊,为什么VLOOKUP执行这么快,当然是因为它是编译好的,不是用VBA写的[3]。你灵机一动,为什么不用这个函数呢,在VBA中,可以使用Application.函数名,调用Excel的内置函数。这样,改过的代码如下:

Sub SelectDouble3()

Dim i As Long, j As Long, a, b

For i = 2 To 9999 Step 1

a = Application.VLookup(Range("A" & i), Range("A1:B" & (i - 1)), 2, False)

b = Application.VLookup(Range("A" & i), Range("A" & (i + 1) & ":B1000"), 2, False)

If IsError(a) And IsError(b) Then

Range("G" & i).Value = 0

End If

Next i

End Sub

代码很短,但有一点复杂和讨厌,循环是从2到9999,因为为了防止VLOOKUP函数的Range范围失效,所以这两行需要手动处理。IsError函数来检测返回值,如果两个返回值都是错误,则此行为单一的没有重复的行,标志为0即可。程序执行速度和上面的差不多,至少你没有感觉出来差别。

4.3.      继续Hack

到这里,你还是觉得不满意,使用数组,数据量太大会内存吃紧,使用VLOOKUP函数,代码觉得很丑陋[4]。你不知道为什么想起来二分查找之类的东东,那么,查找前应该先排序,你在Excel里把数据排了序。现在的问题是需要循环2次,复杂度为N*N,如果…...,你想如果排好了序,只需要检查当前数值和下一个是否一样,如果一样,那么把当前和下一个位置标示出来,循环变量加2,跳过下一个,如果不一样,循环变量加1继续比较就可以了,代码如下:

Sub SelectDouble4()

Dim i As Long, Max As Long

Max = 10000

i = 1

Do

If Range("A" & i).Value = Range("A" & (i + 1)).Value Then

Range("I" & i).Value = 1

Range("I" & (i + 1)).Value = 1

i = i + 2

Else

i = i + 1

End If

Loop While i < Max

End Sub

这个程序复杂度只有N,执行速度当然是你今天写的所有程序里最快的,而且内存占用也最小。你觉得很满意,露出了贼贼的笑容。

5.       总结

你打开了日志,开始记下了今天问题的解决过程。

你想,嗯,如果只是想怎样把Range函数变快来解决问题,速度不会有本质的提高。速度提高,第一,排序才是关键,快速的查找和搜索都是要基于排好序的内容,比如二分查找,那么,为什么数据库要建索引,索引的有无对于查找速度影响很大,道理都是一样的了;第二,查找时没有回溯,对于查找过的内容直接跳过,这个和字符串的匹配算法,好像是KMP算法[5],思路是一样的,嗯,那么如果不是相同的内容不是2个,是多个,那么你可以使用一个循环来前溯,并且,对于不同的个数,可以标识为不同的数字。你忽然觉得自信满满,似乎要忘了已经失业半年的事实。


[1] 在表格或数值数组的首列查找指定的数值,并由此返回表格或数组当前行中指定列处的数值。当比较值位于数据表首列时,可以使用函数 VLOOKUP 代替函数 HLOOKUP。具体用法可以参考Excel帮助。

[2] 作为程序员的你,一直觉得IF函数之类是浪费时间和多此一举,7层的IF函数怎么看得懂?但函数代表简单,你不想因为告诉你同学要写程序解决问题而把他吓坏。

[3] 天知道微软用什么写的这些代码,也许是C,也许是C++,肯定不是Basic,也不是C#,写它时C#还没有出生呢。

[4]或许是你没有写好。

[5] 虽然不是科班出身,你也学过数据结构和算法的。

在Excel中使用VBA来筛选数据相关推荐

  1. Microsoft Excel 教程:如何在 Excel 中使用切片器筛选数据?

    欢迎观看 Microsoft Excel 教程,小编带大家学习 Microsoft Excel 的使用技巧,了解如何在 Excel 中使用切片器筛选数据. 切片器提供可单击以筛选表或数据透视表的按钮. ...

  2. excel中使用VBA如何统计数据区域最后一行行号?

    统计数据区域最后一行行号,可以说是入门VBA时最基础的一行代码,可以让你获取某列或者某一区域的最后一行的行号,本文给出7中不同的方法,得出数据区域最后一行行号,当然每种方法之间有些许差别. 工具/原料 ...

  3. 厌倦了SE11/SE16N? 告诉你如何在Excel中查看SAP的表数据

    SAP 提供 SE11 / SE16 / SE16N 查看表数据,SE11 首先进入的是表结构界面,然后再跳转到选择屏幕,在界面中输入选择条件运行,或直接运行,才能看到表数据.如果表设置了表维护生成器 ...

  4. webbrowser控件 有数据 但页面空白_如何在Excel中实现可以切换不同数据系列的滚珠图?...

    ▲更多精彩内容,请点击上方Excel小铲子▲ 操作系统版本 Windows 10 64位 Excel版本 Microsoft Excel 2016 64位 案例文档下载 链接:https://pan. ...

  5. vba mysql 自动化错误_在Excel中运行VBA脚本时出现自动化错误

    在Excel 2007中运行VBA代码时出现自动化错误.我尝试连接到远程SQL Server数据库并将数据从Excel加载到SQL Server.在Excel中运行VBA脚本时出现自动化错误 我得到的 ...

  6. python 表格格式输出_利用python对excel中一列的时间数据更改格式操作

    问题场景:需要将下列的交期一列的数据格式更改成2019/05/10 存货编码 尺寸 数量 交期 0 K10Y0190000X B140 200 2019-05-10 00:00:00 1 K10Y01 ...

  7. vba编程把纯文本转换成html,如何将一列文本与html标签转换为Excel中的vba格式文本...

    我想知道如何使用VBA脚本将带有html标签的整列单元转换为格式化文本(基于这些标签).如何将一列文本与html标签转换为Excel中的vba格式文本 使用下列内容: Sub Sample() Dim ...

  8. 在集合中根据条件来筛选数据

    在集合中根据条件来筛选数据 from random import randintdata = [randint(-10,10) for _ in range(10)] print(data) s = ...

  9. 在字典中根据条件来筛选数据

    在字典中根据条件来筛选数据 from random import randint #生成一个字典 d = {x:randint(60,100) for x in range(1,11)} print( ...

最新文章

  1. mybatis 配置文件中set丢失逗号
  2. Javascript-稳妥构造函数模式
  3. 转:IDEA 创建类注释模板和方法注释模板
  4. JS实现大整数乘法(性能优化、正负整数)
  5. (转)数字格式化函数:Highcharts.numberFormat()
  6. HDU 1004 Let the Balloon Rise (map)
  7. stage java_Stage 1 - Course 1 - JAVA基础知识
  8. js检测是否安装java_js判断当前浏览器是否是源生app的webview
  9. iOS之iCloud云存档实现笔记
  10. c#与mysql教程_C#连接MySQL操作详细教程
  11. uniapp 运行到手机或模拟器
  12. windows2012计算机在桌面显示,Windows2012 如何在桌面上显示”我的电脑”
  13. Linux下GoldenDict的安装与使用
  14. html5 拉弓,瞄准 拉弓 射出梦想
  15. 吃货制霸地图生成 美食标注 中国制霸数据生成器
  16. Excel学习日记:L27-数据重复怎么办
  17. 计算机键盘手指放置,键盘上手指放置的位置图
  18. Pr 中英文版本切换批处理文件
  19. Unity DOTS 学习笔记1 - ECS 0.50介绍和安装
  20. 杰理6905A芯片引脚的设置

热门文章

  1. VC无负担实现XP风格界面 [转]
  2. 立创开源 BGA162-809H
  3. 基于JSP的汽车配件销售管理系统
  4. python中elif和else的区别_浅谈Python的条件判断语句if/else语句
  5. hdu 1493 qq宠物
  6. MySQL实战——表、索引创建与优化
  7. hiho 大礼堂地毯(strstr 查找函数)
  8. 每一个c语言程序允许有多个函数,一个c语言程序由什么构成
  9. 机器学习水果识别——python+opencv实现物体特征提取
  10. 【持续更新】数据库常用函数整理