英文:
Excel VBA, array range issue
问题
我有以下代码,基于“Main”工作表中单元格B5中的用户输入,将搜索输入的零件号码在另外两个工作表,“SHORTAGE”和“PPN”中,并返回相应的数据列和行。代码有效,但我需要更改shortageData数组的数据范围,从列B到F,改为从列A到F,再加上列L和N。我已经进行了研究并尝试了一些方法,比如合并范围Union(promptSheet.Range("A1:F" & promptLastRow), promptSheet.Range("L1:L" & promptLastRow), promptSheet.Range("N1:N" & promptLastRow)).Value
,以及promptData = promptSheet.Range("A1:F" & promptLastRow & ", L1:L" & promptLastRow & ", N1:N" & promptLastRow).Value
,但都不起作用,它会返回A:F的正确值,但对于接下来的两列L和N,它返回所有的#REF!值。我尝试使用chatGPT也无法解决。是否有人能够协助解决这个问题?非连续范围有关吗?
Sub Button2_Click()
Dim partNum As String
Dim mainSheet As Worksheet
Dim shortageSheet As Worksheet
Dim ppnSheet As Worksheet
Dim mainLastRow As Long
Dim shortageLastRow As Long
Dim ppnLastRow As Long
Dim shortageData As Variant
Dim ppnData As Variant
Dim i As Long
Dim recordFound As Boolean
Application.ScreenUpdating = False
Set mainSheet = ThisWorkbook.Sheets("Main")
Set shortageSheet = ThisWorkbook.Sheets("SHORTAGE")
Set ppnSheet = ThisWorkbook.Sheets("PPN")
partNum = mainSheet.Range("B5").Value
If partNum = "" Then
MsgBox "Please enter a part number.", vbExclamation
Exit Sub
End If
mainSheet.Range("B11:F" & mainSheet.Cells(mainSheet.Rows.Count, "B").End(xlDown).Row).ClearContents
mainSheet.Range("I11:O" & mainSheet.Cells(mainSheet.Rows.Count, "I").End(xlDown).Row).ClearContents
shortageLastRow = shortageSheet.Cells(shortageSheet.Rows.Count, "B").End(xlUp).Row
shortageData = shortageSheet.Range("A1:F" & shortageLastRow & ", L1:L" & shortageLastRow & ", N1:N" & shortageLastRow).Value
For i = 1 To shortageLastRow
If shortageData(i, 1) = partNum Then
mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "B").End(xlUp).Row + 1
mainSheet.Range("B" & mainLastRow & ":I" & mainLastRow).Value = _
Application.Index(shortageData, i, Array(1, 2, 3, 4, 5))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in SHORTAGE"
End If
recordFound = False
ppnLastRow = ppnSheet.Cells(ppnSheet.Rows.Count, "G").End(xlUp).Row
ppnData = ppnSheet.Range("G1:N" & ppnLastRow).Value
For i = 1 To ppnLastRow
If ppnData(i, 1) = partNum Then
mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "I").End(xlUp).Row + 1
mainSheet.Range("I" & mainLastRow & ":O" & mainLastRow).Value = _
Application.Index(ppnData, i, Array(1, 2, 3, 4, 5, 6, 7, 8))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in PPN"
End If
Application.CutCopyMode = False
Application.ScreenUpdating = True
MsgBox "Search complete!", vbInformation
End Sub
请注意,我已经修改了代码,以便在加载shortageData和ppnData时包括了列L和N。希望这对你有所帮助。
英文:
I have the below code that based on user input in cell B5 in "Main" worksheet will search the input part number in another two worksheets, "SHORTAGE" and "PPN", and return the corresponding columns and rows of data, the code works but I need to change the data range for shortageData array from columns B to F, to A to F, plus L, and N. I have researched and tried a few methods, like union range Union(promptSheet.Range("A1:F" & promptLastRow), promptSheet.Range("L1:L" & promptLastRow), promptSheet.Range("N1:N" & promptLastRow)).Value
, and promptData = promptSheet.Range("A1:F" & promptLastRow & ", L1:L" & promptLastRow & ", N1:N" & promptLastRow).Value
, but none works, it returns the correct values for A:F, but for the next two columns L, and N, it returns all #REF! values. Tried using chatGPT and too wasn't able to get a fix. Is anyone able to assist in this? Any help is much appreciated! Does it have to do with non-contiguous range?
Sub Button2_Click()
Dim partNum As String
Dim mainSheet As Worksheet
Dim shortageSheet As Worksheet
Dim ppnSheet As Worksheet
Dim mainLastRow As Long
Dim shortageLastRow As Long
Dim ppnLastRow As Long
Dim shortageData As Variant
Dim ppnData As Variant
Dim i As Long
Dim recordFound As Boolean
Application.ScreenUpdating = False
Set mainSheet = ThisWorkbook.Sheets("Main")
Set shortageSheet = ThisWorkbook.Sheets("SHORTAGE")
Set ppnSheet = ThisWorkbook.Sheets("PPN")
partNum = mainSheet.Range("B5").Value
If partNum = "" Then
MsgBox "Please enter a part number.", vbExclamation
Exit Sub
End If
mainSheet.Range("B11:F" & mainSheet.Cells(mainSheet.Rows.Count, "B").End(xlDown).Row).ClearContents
mainSheet.Range("I11:O" & mainSheet.Cells(mainSheet.Rows.Count, "I").End(xlDown).Row).ClearContents
shortageLastRow = shortageSheet.Cells(shortageSheet.Rows.Count, "B").End(xlUp).Row
shortageData = shortageSheet.Range("B1:F" & shortageLastRow).Value
For i = 1 To shortageLastRow
If shortageData(i, 1) = partNum Then
mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "B").End(xlUp).Row + 1
mainSheet.Range("B" & mainLastRow & ":I" & mainLastRow).Value = _
Application.Index(shortageData, i, Array(1, 2, 3, 4, 5))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in SHORTAGE"
End If
recordFound = False
ppnLastRow = ppnSheet.Cells(ppnSheet.Rows.Count, "G").End(xlUp).Row
ppnData = ppnSheet.Range("G1:N" & ppnLastRow).Value
For i = 1 To ppnLastRow
If ppnData(i, 1) = partNum Then
mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "I").End(xlUp).Row + 1
mainSheet.Range("I" & mainLastRow & ":O" & mainLastRow).Value = _
Application.Index(ppnData, i, Array(1, 2, 3, 4, 5, 6, 7, 8))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in PPN"
End If
Application.CutCopyMode = False
Application.ScreenUpdating = True
MsgBox "Search complete!", vbInformation
End Sub
答案1
得分: 1
这个函数非常长且复杂,所以我会首先进行重构。首先,你应该提取两个类似这样的函数:
Option Explicit
Public Sub Button2_Click()
Application.ScreenUpdating = False
Dim mainSheet As Worksheet: Set mainSheet = ThisWorkbook.Sheets("Main")
mainSheet.Range("B11:F" & mainSheet.Cells(mainSheet.Rows.Count, "B").End(xlDown).Row).ClearContents
mainSheet.Range("I11:O" & mainSheet.Cells(mainSheet.Rows.Count, "I").End(xlDown).Row).ClearContents
Dim partNum As String: partNum = mainSheet.Range("B5").Value
If partNum = "" Then
MsgBox "Please enter a part number.", vbExclamation
Exit Sub
End If
SearchShortage mainSheet, partNum
SearchPpn mainSheet, partNum
Application.CutCopyMode = False
Application.ScreenUpdating = True
MsgBox "Search complete!", vbInformation
End Sub
Public Sub SearchShortage(mainSheet As Worksheet, partNum As String)
Dim shortageSheet As Worksheet: Set shortageSheet = ThisWorkbook.Sheets("SHORTAGE")
Dim shortageLastRow As Long: shortageLastRow = shortageSheet.Cells(shortageSheet.Rows.Count, "B").End(xlUp).Row
Dim shortageData As Variant: shortageData = shortageSheet.Range("B1:F" & shortageLastRow).Value
Dim recordFound As Boolean: recordFound = False
Dim i As Long: For i = 1 To shortageLastRow
If shortageData(i, 1).Value = partNum Then
Dim mainLastRow As Long: mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "B").End(xlUp).Row + 1
mainSheet.Range("B" & mainLastRow & ":I" & mainLastRow).Value = _
WorksheetFunction.Index(shortageData, i, Array(1, 2, 3, 4, 5))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in SHORTAGE"
End If
End Sub
Public Sub SearchPpn(mainSheet As Worksheet, partNum As String)
Dim ppnSheet As Worksheet: Set ppnSheet = ThisWorkbook.Sheets("PPN")
Dim ppnLastRow As Long: ppnLastRow = ppnSheet.Cells(ppnSheet.Rows.Count, "G").End(xlUp).Row
Dim ppnData As Variant: ppnData = ppnSheet.Range("G1:N" & ppnLastRow).Value
Dim recordFound As Boolean: recordFound = False
Dim i As Long: For i = 1 To ppnLastRow
If ppnData(i, 1).Value = partNum Then
Dim mainLastRow As Long: mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "I").End(xlUp).Row + 1
mainSheet.Range("I" & mainLastRow & ":O" & mainLastRow).Value = _
WorksheetFunction.Index(ppnData, i, Array(1, 2, 3, 4, 5, 6, 7, 8))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in PPN"
End If
End Sub
然后,我会继续查找SearchShortage
和SearchPpn
之间的相似之处,并创建一个更抽象的函数,该函数使用不同的参数进行调用。但由于我没有你的工作簿,无法测试任何修改。我还看到一些可能会导致错误的不一致之处(例如,mainSheet
的B:I范围(8列)被用5个值覆盖,而其I:O范围(7列)被用8个值覆盖)。因此,我将跳过这部分。
接下来,我想要更准确地了解你的问题。根据你的描述,我认为你想添加一个函数,我们称之为SearchPrompt
,它有类似的任务,如下所示:
Public Sub Searchprompt(mainSheet As Worksheet, partNum As String)
Dim promptSheet As Worksheet: Set promptSheet = ThisWorkbook.Sheets("PROMPT")
Dim promptLastRow As Long: promptLastRow = promptSheet.Cells(promptSheet.Rows.Count, "B").End(xlUp).Row
Dim promptData As Variant: promptData = Union(promptSheet.Range("A1:F" & promptLastRow), promptSheet.Range("L1:L" & promptLastRow), promptSheet.Range("N1:N" & promptLastRow)).Value,
Dim recordFound As Boolean: recordFound = False
Dim i As Long: For i = 1 To promptLastRow
If promptData(i, 1).Value = partNum Then
Dim mainLastRow As Long: mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "S").End(xlUp).Row + 1
mainSheet.Range("S" & mainLastRow & ":Z" & mainLastRow).Value = _
WorksheetFunction.Index(promptData, i, Array(1, 2, 3, 4, 5, 6, 7, 8))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in prompt"
End If
End Sub
问题是前面六个单元格(在上面的示例中,S:X,我猜测是B:F用于Shortage,然后是2个空列,I:P用于PPN,然后是2个空列,最后是S:Z用于prompt)填充了正确的值,而最后两个(Y:Z)填充了#REF!
。
你已经正确注意到这可能是由于非连续范围引起的。我认为这可能是原因,Union
创建一个Range
对象,其.Areas
属性中有多个“子范围”,如果你引用其.Value
,那么它会被解释为.Areas(1).Value
,其余部分(例如.Areas(2)
)会被忽略。
现在有两个解决方案:
(1)在你的代码中遍历这些.Areas
- 这比另一个解决方案稍微困难一些,我对你的工作表了解不够,无
英文:
This function is quite long and complicated, so I would start with refactoring it. First of all, you should extract two functions like this:
Option Explicit
Public Sub Button2_Click()
Application.ScreenUpdating = False
Dim mainSheet As Worksheet: Set mainSheet = ThisWorkbook.Sheets("Main")
mainSheet.Range("B11:F" & mainSheet.Cells(mainSheet.Rows.Count, "B").End(xlDown).Row).ClearContents
mainSheet.Range("I11:O" & mainSheet.Cells(mainSheet.Rows.Count, "I").End(xlDown).Row).ClearContents
Dim partNum As String: partNum = mainSheet.Range("B5").Value
If partNum = "" Then
MsgBox "Please enter a part number.", vbExclamation
Exit Sub
End If
SearchShortage mainSheet, partNum
SearchPpn mainSheet, partNum
Application.CutCopyMode = False
Application.ScreenUpdating = True
MsgBox "Search complete!", vbInformation
End Sub
Public Sub SearchShortage(mainSheet As Worksheet, partNum As String)
Dim shortageSheet As Worksheet: Set shortageSheet = ThisWorkbook.Sheets("SHORTAGE")
Dim shortageLastRow As Long: shortageLastRow = shortageSheet.Cells(shortageSheet.Rows.Count, "B").End(xlUp).Row
Dim shortageData As Variant: shortageData = shortageSheet.Range("B1:F" & shortageLastRow).Value
Dim recordFound As Boolean: recordFound = False
Dim i As Long: For i = 1 To shortageLastRow
If shortageData(i, 1).Value = partNum Then
Dim mainLastRow As Long: mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "B").End(xlUp).Row + 1
mainSheet.Range("B" & mainLastRow & ":I" & mainLastRow).Value = _
WorksheetFunction.Index(shortageData, i, Array(1, 2, 3, 4, 5))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in SHORTAGE"
End If
End Sub
Public Sub SearchPpn(mainSheet As Worksheet, partNum As String)
Dim ppnSheet As Worksheet: Set ppnSheet = ThisWorkbook.Sheets("PPN")
Dim ppnLastRow As Long: ppnLastRow = ppnSheet.Cells(ppnSheet.Rows.Count, "G").End(xlUp).Row
Dim ppnData As Variant: ppnData = ppnSheet.Range("G1:N" & ppnLastRow).Value
Dim recordFound As Boolean: recordFound = False
Dim i As Long: For i = 1 To ppnLastRow
If ppnData(i, 1).Value = partNum Then
Dim mainLastRow As Long: mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "I").End(xlUp).Row + 1
mainSheet.Range("I" & mainLastRow & ":O" & mainLastRow).Value = _
WorksheetFunction.Index(ppnData, i, Array(1, 2, 3, 4, 5, 6, 7, 8))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in PPN"
End If
End Sub
Then, I would go on with finding similarities between SearchShortage
and SearchPpn
and creating a more abstract function, which is called with different parameters. However, since I do not have your workbook, I cannot test any modification. And I see some inconsistencies that may cause error (e.g. mainSheet's B:I range (8 columns) is overwritten with 5 values, while its I:O range (7 columns) is overwritten with 8 values). So I 'll skip this part.
Next, I'd like to understand your problem exactly. Based on what you wrote, I think you would like to add a function, let's call it SearchPrompt
that has a similar task, like this:
Public Sub Searchprompt(mainSheet As Worksheet, partNum As String)
Dim promptSheet As Worksheet: Set promptSheet = ThisWorkbook.Sheets("PROMPT")
Dim promptLastRow As Long: promptLastRow = promptSheet.Cells(promptSheet.Rows.Count, "B").End(xlUp).Row
Dim promptData As Variant: promptData = Union(promptSheet.Range("A1:F" & promptLastRow), promptSheet.Range("L1:L" & promptLastRow), promptSheet.Range("N1:N" & promptLastRow)).Value,
Dim recordFound As Boolean: recordFound = False
Dim i As Long: For i = 1 To promptLastRow
If promptData(i, 1).Value = partNum Then
Dim mainLastRow As Long: mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "S").End(xlUp).Row + 1
mainSheet.Range("S" & mainLastRow & ":Z" & mainLastRow).Value = _
WorksheetFunction.Index(promptData, i, Array(1, 2, 3, 4, 5, 6, 7, 8))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in prompt"
End If
End Sub
And the problem is that the first six cells (in the above example, S:X as I guessed, that B:F is for Shortage, then 2 empty columns, I:P for PPN, then 2 empty columns, and finally S:Z for prompt) are filled with the correct values, while the last two (Y:Z) are filled with #REF!
.
You have already correctly noted that this may be caused by the non-contiguous range. I suppose that is the case, Union
creates a Range
object that has multiple "subranges" in its .Areas
property, and if you refer to its .Value
, then it is interpreted as .Areas(1).Value
, and the rest (e.g. .Areas(2)
is ignored).
Now there are two solutions:
(1) Iterate over these .Areas
in your code - this is a little bit more difficult than the other solution, and I do not know enough about your worksheet to make a good guess about it.
(2) Simply use a contiguous range. You can search in the range A:N, and then later (when presenting the results) omit the cells that you don't need. You can do that by customizing the numbers in the array supplied as the third parameter of Application.Index
(more correctly, WorksheetFunction.Index
), like this:
Public Sub Searchprompt(mainSheet As Worksheet, partNum As String)
Dim promptSheet As Worksheet: Set promptSheet = ThisWorkbook.Sheets("PROMPT")
Dim promptLastRow As Long: promptLastRow = promptSheet.Cells(promptSheet.Rows.Count, "B").End(xlUp).Row
Dim promptData As Variant: promptData = promptSheet.Range("A1:N" & promptLastRow).Value
Dim recordFound As Boolean: recordFound = False
Dim i As Long: For i = 1 To promptLastRow
If promptData(i, 1).Value = partNum Then
Dim mainLastRow As Long: mainLastRow = mainSheet.Cells(mainSheet.Rows.Count, "S").End(xlUp).Row + 1
mainSheet.Range("S" & mainLastRow & ":X" & mainLastRow).Value = _
WorksheetFunction.Index(promptData, i, Array(1, 2, 3, 4, 5, 6, 12, 14))
recordFound = True
End If
Next i
If Not recordFound Then
MsgBox "No records found in prompt"
End If
End Sub
Note that WorksheetFunction.Index
is called with Array(1, 2, 3, 4, 5, 6, 12, 14)
: 1 .. 6 refer to A .. F, 12 to L and 14 to N.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论