在VBA中运行macros之前检查工作表是否已更新

我正在写一个macros,在那里有一个中央input表 – 让我们把这个表格 – “主要input表”,用户input有关的variables。 在“主input表”中有一些input说 – “任何更多的input表? – 当“是”时,显示与input相对应的工作表(先前已隐藏) – 让我们称之为“关联input表”。 现在,我想确保用户在运行macros之前更新“关联input表”。 有没有办法可以做到这一点 – 使用VBA提供或使用任何其他方式的事件处理程序?

有一个Worksheet_change事件可能会做你想要的:

Private Sub Worksheet_Change(ByVal Target As Range) End Sub 

将其放置在“主要信息表”的代码中,并在每次更改表单时运行。

但是,如果您不希望电子表格每次都运行,工作表会更新,但只想检查是否已更新…您可以做的是创build一个像这样的全局variables(声明必须放置在一个标准模块中:

 Global MainSheetHasChanged as Boolean 

然后,您只需将这行代码放在worksheet_changedmacros中:

 Private Sub Worksheet_Change(ByVal Target As Range) MainSheetHasChanged = True End Sub 

只要确保在运行其他macros之后总是将该variables设置为false。 这是你在找什么?

Worksheet_Change事件过程可能是要走的路,除非你在其他地方发生其他东西发生了很多变化。

在这一点上,你的问题可以改写:“我检查过后,我的范围是否改变了?

抓取范围的副本并将其存储在某个地方,然后根据caching的副本(逐个单元格)检查当前范围是一种暴力方法:如果您只做一次就可以,但如果您正在做它重复地存储一个散列效率更高 – 由某种校验和函数产生的短代码或数字。

校验和algorithm有所不同。 Adler32简单快捷,但性能不佳 – 你得到“散列冲突”或者不能返回不同的哈希数据input不同 – 比较(比方说)一对单字6-10个字母。 然而,当要求检测一列24个8个字母的单词或者几千个date和数字的表格时,它确实performance得非常好。

查看其他哈希值 – 并保持最新:您的PC将拥有多个类似MD5和sha1的哈希库,这些哈希值应该比VBA中的手动哈希值执行得更好。

这是一些使用Adler-32校验和的演示代码。 阅读代码评论,那里有东西,你需要知道为了适应你的项目:


公共函数RangeHasChanged()作为布尔值

'使用下面的Checksum()函数的演示function。

“对于更高级的用户,我在网站上有一个”观看范围“课程:
' http://excellerando.blogspot.com

“作者:Nigel Heffernan,2006年5月http://excellerando.blogspot.com

'请注意,这段代码是在公共领域。 用清楚的标记
'作者的名字,并从任何专有代码分离,如果你需要的话
对该专有代码声明所有权和商业机密

'编码说明:

“预计这个function将被保存在主机工作表中
'模块并重命名以指示正在监视的范围或表格。 它是
使用命名范围而不是硬编码地址是个好主意。

'你也可以select编辑'1到255'到你的范围的宽度。

初始化静态值,以便在VBA会话中进行第一次检查
'不会自动注册一个'更改'就剩下作为练习了
'读者:但是打开工作簿的function就行得通

'这是为了在VBA中使用,而不是在工作表上使用。 使用
'设置'Option Private Module'来隐藏function向导。

Dim rngData As Excel.Range
Dim arrData As Variant

Dim lng Checksum As Long
静态lng持续存在

'请注意,我们捕获一个数组中的整个范围,然后在数组上工作:
“这是一个单一的'打'到表(任何交互操作缓慢
'与工作表数据)与VBA中的所有后续处理。

设置rngData = ThisWorkbook.Names(“DataEntryMain”)。RefersToRange
arrData = rngData.Value2

RangeHasChanged = False

lngChecksum = CheckSum(arrData)

擦除arrData

当文件打开时,lngExisting为零,
“VBA项目被重新初始化,清除所有variables。
“这些事件都不应该被报告为”变化“。

如果lngExisting <> lngChecksum AND lngExisting <> 0那么
RangeHasChanged = True
万一

lngExisting = lngChecksum

结束function

我可以发誓,我在这里发布了这个,多年前,但这里是32位VBA中的Adler-32的实现。

其中有一个可怕的诡计:Adler-32返回一个32位的整数,而VBA Long是一个范围为±(2 ^ 31)-1的有符号整数,所以我实现了一个“环绕”溢出在+ 2 ^ 31,在-2 ^ 31 +1重新启动。 做了一件我真的,真的不应该做一个浮点variables。 最终,每个人​​,到处都有64位的Office,这将是一种古怪和不必要的…对吗?

当然,真正的问题是:为什么要麻烦?

它归结为检查变化的常见问题:如果您不想使用“on change”事件,或者在VBA直接处理数据之前处理数据,则大数据集需要比逐项蛮力的方法。 至less,如果你不止一次地这样做:将每个项目滚动到你的哈希中的成本总是比一对一的比较成本更高…

…如果您要从MySQL或其中一个web API库(如果可以获得暴露的函数,请尝试MDA5)导入快速哈希algorithm,这仍然是正确的,除非您可以find直接读取VBA变体arrays的内容并解除你的VBA线程枚举列表值到导入函数的任务。

同时,下面是一个VBA范围内的哈希algorithm:Adler32。 详情请见维基百科关于Adler32的文章: http : //en.wikipedia.org/wiki/Adler-32 ,一个小时的testing会教你一些关于哈希的知识:

  1. “散列冲突”(不同的数据集返回相同的散列码)比您预期的更常见,特别是对于包含重复模式(如date)的数据;
  2. 哈希algorithm的select很重要;
  3. …而select更多的是一门艺术而不是一门科学。
  4. 承认你真的不应该打扰和诉诸暴力往往是勇气的更好的一部分。

实际上,Adler-32作为教授这些课程的一个工具比作为一个日常的校验和更有用。 检测超过100个不同项目的列表中的变化是很好的; 它是可以容忍的,在24个随机生成的8个字母的单词列表(1800次尝试1次散列冲突),它开始给你一个50位不太清晰的选项到期日,其中差异主要在最后10个字符, 10个字符是经常性的3个月到期date。

在比较6个字母的string对时,超过10%的更改将被非随机数据集中的校验和忽略。 然后你意识到,不pipe怎样,这样的平凡计算也可以用string比较。

所以答案总是:testing它。

同时,这是algorithm,可怕的黑客和一切:


公共函数CheckSum(ByRef ColArray As Variant)只要
Application.Volatile False

'返回列中所有数值和文本值的Adler32校验和

'从单元格获取数据myRange.Value2并使用32位校验和来查看
如果范围中的任何值随后发生变化。 你可以在multithreading
'列范围,但是对于每个列单独运行这个速度要快得多

'请注意,VBA长整数数据types不是一个32位整数,它是一个
'有符号整数范围为±(2 ^ 31)-1。 所以我们的返回值是签名的
'并返回值超过+ 2 ^ 31 -1'环绕'并重新启动-2 ^ 31 +1。

'编码说明:

'这是为了在VBA中使用,而不是在工作表上使用。 使用
'设置'Option Private Module'来隐藏function向导中的CheckSum

“作者:Nigel Heffernan,2006年5月http://excellerando.blogspot.com
感谢推荐Adler-32的Paul Crowley

'请注意,这段代码是在公共领域。 用清楚的标记
'作者的名字,并从任何专有代码分离,如果你需要的话
“对您的专有代码声明所有权和商业机密

Const LONG_LIMIT As Long =(2 ^ 31) – 1
Const MOD_ADLER As Long = 65521

昏暗一长
Dim b As Long

昏暗我只要
Dim j As Long
Dim k As Long

Dim arrByte()As Byte

Dim dblOverflow As Double

如果TypeName(ColArray)=“Range”那么
ColArray = ColArray.Value2
万一

如果IsEmpty(ColArray)那么
CheckSum = 0
退出function
万一

如果(VarType(ColArray)和vbArray)= 0那么
单细胞范围或标量数据types
ReDim arrData(0到0,0到0)
arrData(0,0)= CStr(ColArray)
其他
arrData = ColArray
万一

a = 1
b = 0

对于j = LBound(arrData,2)到UBound(arrData,2)
对于I = LBound(arrData,1)到UBound(arrData,1)

'VBAstring是字节数组:arrByte(n)比Mid $(s,n)快,

arrByte = CStr(arrData(i,j))'这种types转换是否有效?

对于k = LBound(arrByte)到UBound(arrByte)
a =(a + arrByte(k))Mod MOD_ADLER
b =(b + a)Mod MOD_ADLER
下一个k

'使用'vTab'字符终止每个项目会构build更好的散列
'比vbNullString,它等于零,不添加任何信息
'到散列,因此允许冲突ABCD + EFGH = ABC + DEFGH
但是,我们希望避免低效的string连接,所以我们
'将终止字符的字节码直接卷入散列:

a =(a + 11)Mod MOD_ADLER'vbVerticalTab = Chr(11)
b =(b + a)Mod MOD_ADLER

接下来我

'滚动列的哈希与终止水平选项卡字符:

a =(a + 9)Mod MOD_ADLER'水平制表符= Chr(9)
b =(b + a)Mod MOD_ADLER

下一个j

在整数计算中使用浮点数? 我们可以逃避,因为
“VBA double的浮点数误差小于2 ^ 32,小于±0.5

dblOverflow =(1#* b * MOD_ADLER)+ a

如果dblOverflow> LONG_LIMIT Then'wraparound 2 ^ 31 to 1-(2 ^ 31)

直到dblOverflow <LONG_LIMIT
dblOverflow = dblOverflow – LONG_LIMIT
循环
CheckSum = 1 + dblOverflow – LONG_LIMIT

其他
CheckSum = b * MOD_ADLER + a
万一

结束function