我发现我工作的办公室花了几周手动浏览一个Excel电子表格,其中包含一个包含> 500,000行的数据库,寻找符合特定条件的重复行。在研究之前不能简单地删除重复项,因为单个错误可能会丢失数十万美元的生产损失。我决定简单地标记它们,并且在这种情况下引用原始行将是最佳答案。因此,我决定通过使用简单的宏来查看宏以查看可以保存多少时间。我正在使用它作为一种编程学习体验,所以请不要在这里#a = 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
答案 0 :(得分:1)
当您询问有关类型错误的评论时。有很多地方可能出现混乱
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
转换,只需获取.Text
属性即可你想要的字符串。
6)我使用了""
而不是空字符串文字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
答案 1 :(得分:0)
因此,将对象与set
(不仅仅是range
)分配是正确的。 cell
是一个对象,可以分配给range
变量。但是当您使用对象的方法和属性时,在这种情况下.Value
,并不意味着返回值是range
对象。
因此,如果你需要知道所有的属性和方法,我强烈推荐microsoft documentation。
因此,当您使用.Value
时,您将获得一个变体(取决于值的类型)。在您的用例中,您只需将其分配给string
,即Dim str as string: str = Cells(1,1).Value
。如果您只想将cell
作为可以引用的对象:Dim cell as Range: Set cell = Cells(1,1)
。现在可以添加所有属性和方法,例如:cell.Value
而不是Cells(1,1).Value
。
其他一些有用的知识。在VBA中不像VB.Net那样,你最好不要混淆,如果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
是当前Worksheet
的{{1}}的集合。
答案 2 :(得分:0)
您也可以使用RemoveDuplicates方法。
'Remove duplicates based on the data in columns; 2 "Kit", 5 "NSN", and 7 "PN".
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(2, 5, 7), Header:=xlYes