Excel VBA 宏类型不匹配错误
Excel VBA macro type mismatch error
我发现我工作的一个办公室花了数周时间手动检查一个 Excel 电子表格,该电子表格包含一个超过 500,000 行的数据库,以查找符合特定条件的重复行。在研究之前不能简单地删除重复项,因为一个错误可能会导致数十万美元的生产损失。我决定简单地标记它们并引用原始行将是这种情况下的最佳答案。所以我决定研究宏,看看使用一个简单的宏可以节省多少时间 。我将其用作编程学习经验,所以请不要 "here's a =function()" 回答。
我写了一个宏,改了好几次都没用(最新的在下面)。我想使用 String 变量,因为无法确定要检查的单元格中输入了什么。以下是我从该站点尝试过、失败过和学到的东西(?):
最初,我尝试声明一个变量,然后将单元格中的值直接附加到它。例如Dim myString As String Set myString = Cells(x, x).Value
但是,我一直收到对象错误。感谢Michael's response here,我了解到你必须使用Range
变量才能使用Set
。
我的下一期遇到了 "type mismatch" 错误。我正在尝试分配一个存储变量并将其与另一个存储变量进行比较,我确信这是导致问题的原因。我最初尝试 Dim myRange As Range, myString As String Set myRange = Cells(x, x).Value myString = myRange
。这显然行不通,所以我尝试使用 CStr()
"change to string" 函数将 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
,这意味着如果您更改了 sheets 到那时你可能不再指的是你想要的 sheet 了。因此,虽然我保留了 Activesheet
,并使用了一个总体 With Activesheet
语句,然后允许我说 .Cells
或 .Range
等,但您应该将其更改为使用显式 sheet 名字。
3) 无论你在哪里使用 Set
关键字,都期望你正在使用一个对象(例如 Range
)。按照你的命名约定,我会说你的意思是
Set NSNrange = Cells(i, 5)
当你说
Set NSNrange = Cells(i, 5).Value
将一个范围设置为另一个范围而不是单元格值。
4) 我已将您的整数更改为长整数。您正在处理超出 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
因此,您将 对象 分配给 set
(不仅仅是 range
)是正确的。 cell
是一个对象,可以分配给 range
变量。但是当你使用对象的 methods 和 properties 时,在这种情况下 .Value
,并不意味着 return值是一个 range
对象。
因此,如果您需要了解所有属性和方法的用途,我强烈推荐 microsoft documentation。
所以当你使用 .Value
时,你会得到一个变体(取决于值的类型)。在您的 use-case 中,您可以将其分配给 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
。根据您的代码所在的模块,您的代码可能会在错误的工作表上运行。 (当其他人 adjust/run 代码时,它也最大限度地减少了错误),但主要是你只需要更改一个变量,如果你想引用另一个 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
是当前Workbook
的Worksheet
的合集。
您也可以使用 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
我发现我工作的一个办公室花了数周时间手动检查一个 Excel 电子表格,该电子表格包含一个超过 500,000 行的数据库,以查找符合特定条件的重复行。在研究之前不能简单地删除重复项,因为一个错误可能会导致数十万美元的生产损失。我决定简单地标记它们并引用原始行将是这种情况下的最佳答案。所以我决定研究宏,看看使用一个简单的宏可以节省多少时间 。我将其用作编程学习经验,所以请不要 "here's a =function()" 回答。
我写了一个宏,改了好几次都没用(最新的在下面)。我想使用 String 变量,因为无法确定要检查的单元格中输入了什么。以下是我从该站点尝试过、失败过和学到的东西(?):
最初,我尝试声明一个变量,然后将单元格中的值直接附加到它。例如Dim myString As String Set myString = Cells(x, x).Value
但是,我一直收到对象错误。感谢Michael's response here,我了解到你必须使用Range
变量才能使用Set
。
我的下一期遇到了 "type mismatch" 错误。我正在尝试分配一个存储变量并将其与另一个存储变量进行比较,我确信这是导致问题的原因。我最初尝试 Dim myRange As Range, myString As String Set myRange = Cells(x, x).Value myString = myRange
。这显然行不通,所以我尝试使用 CStr()
"change to string" 函数将 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
,这意味着如果您更改了 sheets 到那时你可能不再指的是你想要的 sheet 了。因此,虽然我保留了 Activesheet
,并使用了一个总体 With Activesheet
语句,然后允许我说 .Cells
或 .Range
等,但您应该将其更改为使用显式 sheet 名字。
3) 无论你在哪里使用 Set
关键字,都期望你正在使用一个对象(例如 Range
)。按照你的命名约定,我会说你的意思是
Set NSNrange = Cells(i, 5)
当你说
Set NSNrange = Cells(i, 5).Value
将一个范围设置为另一个范围而不是单元格值。
4) 我已将您的整数更改为长整数。您正在处理超出 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
因此,您将 对象 分配给 set
(不仅仅是 range
)是正确的。 cell
是一个对象,可以分配给 range
变量。但是当你使用对象的 methods 和 properties 时,在这种情况下 .Value
,并不意味着 return值是一个 range
对象。
因此,如果您需要了解所有属性和方法的用途,我强烈推荐 microsoft documentation。
所以当你使用 .Value
时,你会得到一个变体(取决于值的类型)。在您的 use-case 中,您可以将其分配给 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
。根据您的代码所在的模块,您的代码可能会在错误的工作表上运行。 (当其他人 adjust/run 代码时,它也最大限度地减少了错误),但主要是你只需要更改一个变量,如果你想引用另一个 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
是当前Workbook
的Worksheet
的合集。
您也可以使用 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