【发布时间】:2019-12-05 03:00:27
【问题描述】:
如何将 sheet-1 的一列的所有值与具有不同 sheet-2 的另一列值进行比较,如果匹配,则将 sheet-1 的一列对应的值返回到 excel 中 sheet-2 的另一列?
【问题讨论】:
-
您希望将第 1 列中的每个值与第 2 列中的每个值进行比较(列的位置没有区别)。如果找到匹配项,不清楚你想做什么
如何将 sheet-1 的一列的所有值与具有不同 sheet-2 的另一列值进行比较,如果匹配,则将 sheet-1 的一列对应的值返回到 excel 中 sheet-2 的另一列?
【问题讨论】:
假设您的值位于名为 Sheet1 和 Sheet2 的工作表的 A 列中。然后,您可以将以下公式放入Sheet2 的B1 并向下拖动以覆盖您想要的范围:=IF(Sheet1!A1=Sheet2!A1,Sheet2!A1,"")
或者,如果您更喜欢使用 VBA,请将此代码放入模块中:
Sub columnCompare()
Dim sh1 As Worksheet, sh2 As Worksheet, r1 As Range, r2 As Range
Set sh1 = Worksheets("Sheet1")
Set sh2 = Worksheets("Sheet2")
Set r1 = sh1.Range("A1")
Set r2 = sh2.Range("A1")
While r1 <> "" And r2 <> ""
If r1 = r2 Then r2.Offset(0, 1) = r1
Set r1 = r1.Offset(1, 0)
Set r2 = r2.Offset(1, 0)
Wend
End Sub
【讨论】:
我只能回答您的部分问题:比较两列并检测它们是否不同。
您在上面的Tony M 的回答中有一个很好的教程答案。
但是,这将在大型数据集上执行得很慢,因为:
var = Range("A1") 拾取单个单元格所花费的时间与使用var = Range("A1:Z1024") 一次性拾取整个范围所花费的时间相同;与工作表的每次交互所花费的时间是 VBA 中字符串比较的四倍,是浮点小数比较的二十倍;反过来,这比整数比较长三倍。
因此,如果您一次读取整个范围,并在 VBA 中处理 Range.Value2 数组,您的代码可能快四倍,甚至可能快一百倍。
这是在 Office 2010 和 2013 中(我测试过它们);对于旧版本的 Excel,对于与单元格或范围细胞。这将*慢**慢,因为在新旧版本的 Excel 中,VBA 操作仍将是个位数的微秒数:您的代码将运行至少快一百倍,甚至可能数千倍如果您避免在旧版本的 Excel 中逐个单元格地读取工作表,则速度会更快。
在一次“点击”中拾取范围,然后在 VBA 中对数组的每个项目进行比较,这样可以获得很大的收益 - 用户可感知的间隔。
arr1 = Range1.Values
arr2 = Range2.Values
' Consider checking that the two ranges are the same size
For i = LBound(arr1, 1) To Ubound(arr1, 2)
For j = LBound(arr1, 2) To Ubound(arr1, 2)
If arr1(i, j) <> arr2(i, j) Then
bMatchFail = True
Exit For
End If
Next j
If bMatchFail Then Exit For
Next i
Erase arr1
Erase arr2
您会注意到此代码示例是通用的,适用于从任何地方获取的两个相同大小的范围 - 甚至来自不同的工作簿。如果您要比较两个相邻的列,则加载一个包含两列的数组并比较 IF arrX(i, 1) <> arrX(i,2) Then 将使运行时间减半。
仅当您从大范围中提取数万个值时,您的下一个挑战才有意义:对于小于此范围的任何值,此扩展答案都没有性能提升。
我们正在做的是:
这个想法非常简单,尽管基础数学对非数学家来说相当具有挑战性:我们不是一次比较一个值,而是运行一个数学函数,将这些值“散列”成一个简短的标识符,以便于比较。
如果您将范围与“参考”副本进行比较,则可以存储“参考”哈希,这样可以将工作量减半。
目前有一些快速可靠的散列函数,它们在 Windows 中作为安全和加密 API 的一部分提供。有一个小问题是它们在字符串上运行,我们有一个数组要处理;但是您可以轻松找到一个快速的“Join2D”函数,该函数从范围的.Value2 属性返回的二维数组中获取字符串。
因此,两个大范围的快速比较函数将如下所示:
Public Function RangeCompare(Range1 as Excel.Range, Range2 As Excel.Range) AS Boolean
' Returns TRUE if the ranges are identical.
' This function is case-sensitive.
' For ranges with fewer than ~1000 cells, cell-by-cell comparison is faster
' WARNING: This function will fail if your range contains error values.
RangeCompare = False
If Range1.Cells.Count <> Range2.Cells.Count Then
RangeCompare = False
ElseIf Range1.Cells.Count = 1 then
RangeCompare = Range1.Value2 = Range2.Value2
Else
RangeCompare = MD5(Join2D(Range1.Value2)) = MD5(Join2D(Range2.Value2))
Endif
End Function
我在这个 VBA 函数中封装了 Windows System.Security MD5 哈希:
还有其他的 VBA 实现,但似乎没有人知道字节数组/字符串类型双关语 - 它们不是 等效,它们是 相同 - 所以每个人都不需要编写代码类型转换。
Public Function MD5(arrBytes() As Byte) As String
' Return an MD5 hash for any string
' Author: Nigel Heffernan Excellerando.Blogspot.com
' Note the type pun: you can pass in a string, there's no type conversion or cast
' because a string is stored as a Byte array and VBA recognises this.
Dim oMD5 As Object 'Set a reference to mscorlib 4.0 to use early binding
Dim HashBytes() As Byte
Dim i As Integer
Set oMD5 = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
HashBytes = oMD5.ComputeHash_2((arrBytes))
For i = LBound(HashBytes) To UBound(HashBytes)
MD5 = MD5 & Right("00" & Hex(HashBytes(i)), 2)
Next i
Set oMD5 = Nothing ' if you're doing this repeatedly, declare at module level and persist
Erase HashBytes
End Function
一个快速简单的 Join2D 函数是 2015 年的posted by Dick Kusleika on Daily Dose of Excel:
Public Function Join2D(ByVal vArray As Variant, Optional ByVal sWordDelim As String = " ", Optional ByVal sLineDelim As String = vbNewLine) As String
Dim i As Long, j As Long
Dim aReturn() As String
Dim aLine() As String
ReDim aReturn(LBound(vArray, 1) To UBound(vArray, 1))
ReDim aLine(LBound(vArray, 2) To UBound(vArray, 2))
For i = LBound(vArray, 1) To UBound(vArray, 1)
For j = LBound(vArray, 2) To UBound(vArray, 2)
'Put the current line into a 1d array
aLine(j) = vArray(i, j)
Next j
'Join the current line into a 1d array
aReturn(i) = Join(aLine, sWordDelim)
Next i
Join2D = Join(aReturn, sLineDelim)
End Function
如果您需要在进行比较之前删除空白行,则需要Join2D function I posted in StackOverflow back in 2012。
这种类型的哈希比较最常见的应用是电子表格控制 - 更改监控 - 您会看到使用 Range1.Formula 而不是 Range1.Value2:但您的问题是关于比较值,而不是公式。
【讨论】: