Excel VBA,数组范围问题

huangapple go评论65阅读模式
英文:

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

然后,我会继续查找SearchShortageSearchPpn之间的相似之处,并创建一个更抽象的函数,该函数使用不同的参数进行调用。但由于我没有你的工作簿,无法测试任何修改。我还看到一些可能会导致错误的不一致之处(例如,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.

huangapple
  • 本文由 发表于 2023年5月28日 16:31:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76350589.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定