我发现我工作的办公室花了几周时间手动浏览一个Excel电子表格,其中包含一个> 500,000行的数据库,寻找符合特定条件的重复行。在研究之前不能简单地删除重复项,因为单个错误可能会丢失数十万美元的生产损失。我决定简单地标记它们,并且在这种情况下引用原始行将是最佳答案。所以我决定查看宏来看看通过使用简单的宏来节省多少时间。我使用它作为编程学习经验,所以请不要“这是一个= = function()”的答案。
我已经写了一个宏并且改了几次都无济于事(目前大多数情况都在下面)。我想使用String变量,因为无法确定将要检查的单元格中输入了什么。这是我从这个网站尝试,失败和学习(?)的内容:
最初,我尝试声明一个变量,并将一个单元格中的值直接附加到它。例如Dim myString As String Set myString = Cells(x, x).Value
然而,我一直遇到对象错误。感谢Michael's回应here,我了解到你必须使用Range
变量来使用Set
。
我的下一个问题是出现“类型不匹配”错误。我正在尝试将存储的变量与另一个存储的变量进行分配和比较,我确信这会导致问题。我最初尝试过Dim myRange As Range, myString As String Set myRange = Cells(x, x).Value myString = myRange
。这显然不起作用,所以我尝试使用CStr()
“更改为字符串”函数将Range变量转换为我想要的String变量。那就是我被困住的地方。
Sub Duplicate()
'Declare the variables
Dim NSNrange, PNrange, KitIDrange As Range
Dim NSN, PN, KitID As String
Dim NSNCheck, PNCheck, KitIDCheck As String
Dim i, j, printColumn, rowCount As Integer
'Set which column we want to print duplicates on, and count the number of rows used
rowCount = ActiveSheet.UsedRange.Rows.Count
printColumn = 9
'Lets get started!
'Clear the duplicate list column for a fresh start
Columns(printColumn).EntireColumn.Delete
'Start on line 2, and grab the cell values for the NSN, Part number and kit ID.
For i = 2 To rowCount
Set NSNrange = Cells(i, 5).Value
Set PNrange = Cells(i, 7).Value
Set KitIDrange = Cells(i, 2).Value
'Change whatever is contained in those cells into a string and store them into their respective containers
NSN = CStr(NSNrange)
PN = CStr(PNrange)
KitID = CStr(KitIDrange)
'Now let's look through the rest of the sheet and find any others that match the 3 variables that we stored above
For j = 2 To rowCount
'To avoid needless checks, we'll check to see if it's already had a duplicate found. If so, we'll just skip to the next row
If Cells(j, printColumn).Value = "" Then
'If the print column is blank, we'll grab the 3 values from the current row to compare against the above variables
Set NSNrange = Cells(j, 5).Value
Set PNrange = Cells(j, 7).Value
Set KitIDrange = Cells(j, 2).Value
'Now we store the contents into their very own container
NSNCheck = CStr(NSNrange)
PNCheck = CStr(PNrange)
KitIDCheck = CStr(KitIDrange)
'Check the initial row with the current row to see if the contents match. If so, print which row it is duplicated on.
If NSN = NSNCheck And PN = PNCheck And KitID = KitIDCheck Then Cells(j, printColumn).Value = "Duplicated on row " & i
End If
Next j
Next i
MsgBox "Search Complete"
End Sub
当您询问有关类型错误的注释时。有很多地方可能出现混乱
1)你在同一行上做多个声明的每一行都是这样的:
Dim NSNrange, PNrange, KitIDrange As Range
只有最后一个变量是显式类型声明的(在本例中为Range
)。其他是隐含的Variant
。所以,我已经完成并单独划线并宣布它们,因为我相信你可能意味着它们。
2)使用Activesheet
,在其他地方,只有Cells
或Range
,隐含地引用Activesheet
,意味着如果你已经改变了纸张,那么你可能会更长时间地指你想要的纸张。因此,虽然我保留了Activesheet
,并使用了一个总体的With Activesheet
声明,然后允许我说.Cells
或.Range
等,你应该改为使用显式的工作表名称。
3)无论你使用Set
关键字,期望是你正在使用一个对象(例如Range
)。按照你的命名惯例,我会说你的意思
Set NSNrange = Cells(i, 5)
当你说
Set NSNrange = Cells(i, 5).Value
这将范围设置为另一个范围而不是单元格值。
4)我已将整数更改为Longs。您正在使用的行可以超出Integer
类型可以处理的行,因此您可能会溢出。 Long
更安全。
5)而不是如下在Range
上进行转换
NSN = CStr(NSNrange)
如果你想要一个字符串,你可以删除.Value
转换,只需要获取CStr
属性,它将为你提供所需的字符串。
6)而不是空字符串文字.Text
比较,我使用""
更快分配和检查。
vbNullString
因此,使用Option Explicit
Sub Duplicate()
Dim NSNrange As Range
Dim PNrange As Range
Dim KitIDrange As Range
Dim NSN As String
Dim PN As String
Dim KitID As String
Dim NSNCheck As String
Dim PNCheck As String
Dim KitIDCheck As String
Dim i As Long
Dim j As Long
Dim printColumn As Long
Dim rowCount As Long
With ActiveSheet
rowCount = .UsedRange.Rows.Count
printColumn = 9
.Columns(printColumn).EntireColumn.Delete
For i = 2 To rowCount
Set NSNrange = .Cells(i, 5)
Set PNrange = .Cells(i, 7)
Set KitIDrange = .Cells(i, 2)
NSN = NSNrange.Text
PN = PNrange.Text
KitID = KitIDrange.Text
For j = 2 To rowCount
If .Cells(j, printColumn).Value = vbNullString Then
Set NSNrange = .Cells(j, 5)
Set PNrange = .Cells(j, 7)
Set KitIDrange = .Cells(j, 2)
NSNCheck = NSNrange.Text
PNCheck = PNrange.Text
KitIDCheck = KitIDrange.Text
If NSN = NSNCheck And PN = PNCheck And KitID = KitIDCheck Then
.Cells(j, printColumn).Value = "Duplicated on row " & i
End If
End If
Next j
Next i
End With
MsgBox "Search Complete"
End Sub
(不仅仅是set
)分配对象是正确的。 range
是一个对象,可以分配给cell
变量。但是当你使用对象的方法和属性时,在这种情况下range
,并不意味着返回值是一个.Value
对象。
因此,如果您需要了解所有属性和方法,我强烈推荐qazxsw poi。
所以当你使用range
时,你会得到一个变量(取决于值的类型)。在您的用例中,您可以将其分配给microsoft documentation,即.Value
。如果你只想要string
as一个你可以参考的对象:Dim str as string: str = Cells(1,1).Value
。现在可以解决所有属性和方法,例如:cell
而不是Dim cell as Range: Set cell = Cells(1,1)
。
还有一些有用的知识。在VBA不像VB.Net,你最好不要混淆,如果你cell.Value
只有Cells(1,1).Value
是Dim var1, var2 as String
,var2
是string
。因此需要为每个变量var1
指定类型。
您可能想要改变的另一件事是将variant
分配给特定的Dim var1 as String, var2 as String
。根据您的代码所在的模块,您可能会在错误的工作表上运行代码。 (当其他人调整/运行代码时,它最大限度地减少了错误),但主要是你只需要改变一个变量,如果你想引用另一个Cells, Range
。可以使用Worksheet
完成。
Worksheet
另请注意,这里的对象是没有Worksheet-Object
的Dim ws as Worksheet
Dim str as String
Set ws = Worksheets(1)
'Now adress methods and properties with ws
str = ws.Cells(1,1).Value
。 Worksheet
是当前s
的Worksheets
的集合。
您还可以使用RemoveDuplicates方法。
Worksheet