比较两列,其中一列不是必需的

Compare two columns from which one column is not mandatory

提问人:Stefanie 提问时间:2/10/2023 最后编辑:Adrian MoleStefanie 更新时间:2/11/2023 访问量:67

问:

我需要宏代码的帮助。

就我而言,excel宏检查一个工作表(“Check_file”)中的数据的完整性和正确性。 工作表中必须存在必填列,也可以存在非必填列。

在我的示例中,“company”和“fee”列是必填列,如果它们缺失或为 false,宏将抛出错误。 在它们旁边,“总费用”列不是强制性的,如果存在“总费用”列,则只能与“费用”列中的数据核对其数据。如果存在,则金额应与“费用”栏中的金额相同。如果它不存在,则不应进行比较。

对强制列的检查在 For 循环和自己的范围内工作正常。

我的问题是我不知道如何将非强制性列纳入强制性列的循环中...... 我试图为非强制性列区域定义一个单独的范围。但是,如果未在强制列循环中设置非强制列,则似乎无法创建与非强制列的连接。但是,如果它设置为必填列范围,并且非必填列不存在,则会引发错误。

是否应该将非强制性列的存在检查放在单独的 Sub 或 Function 中?如果是,如何创建与强制检查范围的连接?

这是 vba 代码:

Function Main_Check(ByVal StrFilePath As String) As String
    '//Checks all criteria for the correct filling of the template. Marks all fields that are incorrectly 
    filled in red.

    Dim WB As Workbook, WS As Worksheet
    Dim i As Long, iNotMand As Long, lEnde As Long, strHeader As String, ii As Long, lColEnde As Long
    Dim rngFind As Range, booCheck As Boolean, rngHeader As Range, rngKey As Range, rngUsed As Range,
    rngHeaderNotMand As Range, rngFindNotMand As Range, rngKeyGrossFee As Range, rngGrossFee As Range
    Dim strKey As String, arrKey As String, strKeyGrossFee As String, strGrossFee As String

    On Error GoTo ErrorHandler

    If StrFilePath = “” Then GoTo ErrorHandler

    With Application
    .ScreenUpdating = False
    .EnableEvents = False
    End With

    '//Template is opened
    Set WB = Workbooks.Open(StrFilePath)
    Set WS = WB.Worksheets(“Check_file”)

    With WS
    .Cells.EntireColumn.AutoFit

    '//Stores the last row and column to be processed
    lEnde = .Cells(.UsedRange.SpecialCells(xlCellTypeLastCell).Row + 2, 2).End(xlUp).Row
    lColEnde = .UsedRange.SpecialCells(xlCellTypeLastCell).Column


    '//Find the beginning of the table
    Set rngFind = .Cells.Find(what:=Settings.Cells(Settings.Range("Header_Start").Row + 1, 2).Value,
    LookIn:=xlValues, lookat:=xlWhole)
    If rngFind Is Nothing Then
         booCheck = False
    End
    End If

    .Range(rngFind.Address, .Cells(.UsedRange.SpecialCells(xlCellTypeLastCell).Row,
    rngFind.Column)).EntireRow.Hidden = False

    lEnde = .Cells(.UsedRange.SpecialCells(xlCellTypeLastCell).Row + 2, 2).End(xlUp).Row

    '//booCheck is set to true and on error to false _
    Thus, if "True" is passed, the complete file is correct

    Set rngUsed = .Range(rngFind.Address, .Cells(lEnde, lColEnde))

    booCheck = IsErrorAll(rngUsed)
        
    '//Header Check _
    Checks all headers in advance to see if they are present and writes the missing ones in a cell
    .Cells(4, 7).Clear
    .Cells(4, 8).Clear
     For i = Settings.Range("Header_Start").Row + 1 To Settings.Range("Header_Ende").Row - 1
    Set rngHeader = .Range(rngFind, .Cells(rngFind.Row, lColEnde)).Find(what:=Settings.Cells(i, 2).Value,
    LookIn:=xlValues, lookat:=xlWhole)
        If rngHeader Is Nothing Then
             booCheck = False
        
             .Cells(4, 7).Value = "The following column labels were not found: "
        
             If .Cells(4, 8).Value = "" Then
                  .Cells(4, 8).Value = .Cells(4, 8).Value & Settings.Cells(i, 2).Value
             Else
                  .Cells(4, 8).Value = "," & .Cells(4, 8).Value & Settings.Cells(i, 2).Value
             End If
             .Cells(4, 8).Interior.Color = vbRed
        
         Else
        
         End If
    
Next i

    If booCheck = False Then GoTo Ende

    '// Check Not-Mandatory Columns _
     Checks in advance whether Not-mandatory columns are available
    Set rngFindNotMand = .Cells.Find(what:=Settings.Cells(Settings.Range("NotMand_Start").Row + 1, 2).Value, LookIn:=xlValues, lookat:=xlWhole)
For iNotMand = Settings.Range("NotMand_Start").Row + 1 To Settings.Range("NotMand_Ende").Row - 1
Set rngHeaderNotMand = .Range(rngFindNotMand, .Cells(rngFindNotMand.Row, lColEnde)).Find(what:=Settings.Cells(iNotMand, 2).Value, LookIn:=xlValues, lookat:=xlWhole)
    If Not rngHeaderNotMand Is Nothing Then
    '//Not-mandatory columns are defined
     strKeyGrossFee = "Gross fee"
     Set rngKeyGrossFee = Settings.Cells(1, 1).EntireColumn.Find(what:=strKeyGrossFee, LookIn:=xlValues, lookat:=xlWhole)
     strGrossFee = Settings.Cells(rngKeyGrossFee.Row, 2).Value
    Else
    strKeyGrossFee = ""
    End If

'//All line items are run through and the individual criteria are checked
For i = rngFind.Row + 1 To lEnde Step 1
    
    '//Company
    strKey = "Company"
    Set rngKey = Settings.Cells(1, 1).EntireColumn.Find(what:=strKey, LookIn:=xlValues, lookat:=xlWhole)
    strHeader = Settings.Cells(rngKey.Row, 2).Value
    Set rngHeader = .Range(rngFind, .Cells(rngFind.Row, lColEnde)).Find(what:=strHeader, LookIn:=xlValues, lookat:=xlWhole)
    iCoi = rngHeader.Column
    If .Cells(i, rngHeader.Column).Value Like "####" Then
        .Cells(i, rngHeader.Column).Interior.Pattern = xlNone
    Else
        .Cells(i, rngHeader.Column).Interior.Color = vbRed
        booCheck = False
    End If
    
    '//Fee
    strKey = "Fee"
    Set rngKey = Settings.Cells(1, 1).EntireColumn.Find(what:=strKey, LookIn:=xlValues, lookat:=xlWhole)
    strHeader = Settings.Cells(rngKey.Row, 2).Value
    Set rngHeader = .Range(rngFind, .Cells(rngFind.Row, lColEnde)).Find(what:=strHeader, LookIn:=xlValues, lookat:=xlWhole)
    If .Cells(i, rngHeader.Column).Value Like "*,*" Then
             .Cells(i, rngHeader.Column).Interior.Color = vbRed
              booCheck = False
    Else
        .Cells(i, rngHeader.Column).Interior.Pattern = xlNone
    End If
            
    '//Gross fee
    strKey = "Fee"
    Set rngKey = Settings.Cells(1, 1).EntireColumn.Find(what:=strKey, LookIn:=xlValues, lookat:=xlWhole)
    strHeader = Settings.Cells(rngKey.Row, 2).Value
    Set rngHeader = .Range(rngFind, .Cells(rngFind.Row, lColEnde)).Find(what:=strHeader, LookIn:=xlValues, lookat:=xlWhole)
    
    Set rngFindNotMand = .Cells.Find(what:=Settings.Cells(Settings.Range("NotMand_Start").Row + 1, 2).Value, LookIn:=xlValues, lookat:=xlWhole)
    strKeyGrossFee = "Gross fee"
    Set rngKeyGrossFee = Settings.Cells(1, 1).EntireColumn.Find(what:=strKeyGrossFee, LookIn:=xlValues, lookat:=xlWhole)
    strGrossFee = Settings.Cells(rngKeyGrossFee.Row, 2).Value
    Set rngGrossFee = .Range(rngFindNotMand, .Cells(rngFindNotMand.Row, lColEnde)).Find(what:=strGrossFee, LookIn:=xlValues, lookat:=xlWhole)
    
    If .Cells(i, rngGrossFee.Column).Value Is Nothing Then
       .Cells(i, rngHeader.Column).Interior.Pattern = xlNone
    ElseIf .Cells(i, rngHeader.Column).Value <> .Cells(i, rngGrossFee.Column).Value Then
       .Cells(i, rngHeader.Column).Interior.Color = vbRed
           booCheck = False
    Else
       .Cells(i, rngHeader.Column).Interior.Pattern = xlNone
    End If         
    
Next i

End With

'//Define results
Ende:

Main_Check = booCheck & “,” & Replace(CStr(rngFind.Address), “$”, “”)

If booCheck = False Then
WS.Cells(7, 7).Value = “Error counter:”
WS.Cells(7, 8).Value = WS.Cells(7, 8).Value + 1
Else
WS.Cells(7, 7).Value = “Check ok”
WS.Cells(7, 8).Value = “”
End If

WB.Close (True)

With Application
.ScreenUpdating = True
.EnableEvents = True
End With

Exit Function

'//If there are other errors, it should exit here and return ERROR
ErrorHandler:
On Error GoTo -1
On Error Resume Next
Main_Check = “ERROR”
WB.Close (True)

With Application
.ScreenUpdating = True
.EnableEvents = True
End With

End Function
Excel VBA 循环 Office365 比较

评论

1赞 CDP1802 2/10/2023
此代码是否编译/运行?好像少了一行,也,应该是? 未定义,不在代码中的任何位置使用。Next iNotMandiCoi = rngHeader.ColumniCoiiColiCoi

答:

0赞 CDP1802 2/11/2023 #1

我建议您将代码重构为 3 个部分。1-阅读设置,2-分析工作表,然后3-检查数据。依次调试每个步骤。

Function Main_Check2(ByVal StrFilePath As String) As String

    Const CHECK = "Check_file"
    
    Dim wb As Workbook, ws As Worksheet, arHdr
    Dim booCheck As Boolean, bExists As Boolean
    Dim iColFee As Long, i As Long, msg As String
    
     ' check valid filepath
    If Dir(StrFilePath) = "" Then
        msg = "'" & StrFilePath & "' does not exist"
        MsgBox msg, vbCritical, "File not found"
        Main_Check2 = msg
        Exit Function
    End If
    
     ' open file and check sheet exists
    Set wb = Workbooks.Open(StrFilePath)
    bExists = False
    For Each ws In wb.Sheets
        If ws.Name = CHECK Then
            bExists = True
            Exit For
        End If
    Next
    
    If Not bExists Then
        msg = "Sheet '" & CHECK & "' not found in " & wb.FullName
        MsgBox msg, vbCritical, "Sheet not found"
        wb.Close False
        Exit Function
    End If
    
    ' get header details from settings sheet
    Call GetSettings(Settings, arHdr)
    
    ' check headers, find fee column
    booCheck = CheckHeaders(ws, arHdr, iColFee)
    'Call DumpArray(arHdr) ' check results so far
    
    ' exit if false
    If booCheck = False Then
         Main_Check2 = booCheck
         Exit Function
    End If
    
    ' check data
    With ws
                
        '//All line items are run through and the individual criteria are checked
        Dim rngData As Range, sName As String, n As Long
        Dim iRow As Long, iCol As Long, lastrow As Long, cell As Range
        
        For i = 1 To UBound(arHdr)
        
            sName = arHdr(i, 1) ' header name
            iCol = arHdr(i, 4) ' header column
            iRow = arHdr(i, 5) ' header row
            n = arHdr(i, 6) ' number of rows
            
            ' scan column
            If iCol > 0 And n > 0 Then
                Set rngData = .Cells(iRow + 1, iCol).Resize(n)
                Select Case sName
                    Case "Company"
                        For Each cell In rngData
                            If cell.Value Like "####" Then
                               cell.Interior.Pattern = xlNone
                            Else
                               cell.Interior.Color = vbRed
                               booCheck = False
                            End If
                        Next
                    
                    Case "Fee"
                        For Each cell In rngData
                            If Not cell.Value Like "*,*" Then
                               cell.Interior.Pattern = xlNone
                            Else
                               cell.Interior.Color = vbRed
                               booCheck = False
                            End If
                        Next
                                    
                    Case "Gross Fee"
                        ' optional - skipped if icol = 0
                        For Each cell In rngData
                            If Len(cell) = 0 Then
                                cell.Interior.Pattern = xlNone
                            ElseIf cell.Value <> .Cells(cell.Row, iColFee).Value Then
                                cell.Interior.Color = vbRed
                                booCheck = False
                            Else
                                cell.Interior.Pattern = xlNone
                            End If
                        Next
                End Select
            End If
        Next
    End With
    
'//Define results
Ende:
    If booCheck = False Then
        ws.Cells(7, 7).Value = "Error counter:"
        ws.Cells(7, 8).Value = ws.Cells(7, 8).Value + 1
    Else
        ws.Cells(7, 7).Value = "Check ok"""
        ws.Cells(7, 8).Value = ""
    End If

    'wb.Close True
    Main_Check2 = booCheck '& "," & Replace(CStr(rngFind.Address), "$", "")

End Function

Function GetSettings(ws, ByRef arHdr) As Boolean

    Dim r1 As Long, r2 As Long, r3 As Long, r4 As Long
    Dim n As Long, m As Long, i As Long, msg As String
    
    With ws
        r1 = .Range("Header_Start").Row
        r2 = .Range("Header_Ende").Row
        r3 = .Range("NotMand_Start").Row
        r4 = .Range("NotMand_Ende").Row
       
        m = r2 - r1 - 1 ' mandatory header
        n = r4 - r3 - 1 ' non-mandatory headers
       
        If m < 1 Then
            msg = "No mandatory headers on setting"
            MsgBox msg, vbExclamation, "Settings Error"
            getSettings = False
        End If
       
        ' size array and fill
        ReDim arHdr(1 To n + m, 1 To 6)
        For i = 1 To m
            arHdr(i, 1) = .Cells(r1 + i, 1)
            arHdr(i, 2) = .Cells(r1 + i, 2) ' search term
            If Len(arHdr(i, 2)) > 0 Then ' skip blanks
                arHdr(i, 3) = True ' mandatory
            Else
                arHdr(i, 3) = False
            End If
        Next
        For i = 1 To n
            arHdr(m + i, 1) = .Cells(r3 + i, 1)
            arHdr(m + i, 2) = .Cells(r3 + i, 2)
            arHdr(m + i, 3) = False ' optional
        Next
                
    End With
    getSettings = True

End Function

Function CheckHeaders(ws, ByRef arHdr, ByRef iColFee) As Boolean

    '//Header Check
    'Checks all headers in advance to see if they are present
    'and writes the missing ones in a cell
    Dim rngTable As Range, rng As Range
    Dim msg As String, sHdr As String, sTableStart As String
    Dim i As Long, lastrow As Long, rowHdr As Long
    Dim booCheck As Boolean
   
    ' search value for column 1 of table
    sTableStart = arHdr(1, 2)
    With ws
         '//Find the beginning of the table
        Set rngTable = .Cells.Find(what:=sTableStart, LookIn:=xlValues, lookat:=xlWhole)
        If rngTable Is Nothing Then
            msg = "Could not find begining of table '" & sTableStart & "'"
            MsgBox msg, vbExclamation, "Error"
            CheckHeaders = False
            Exit Function
        Else
            rowHdr = rngTable.Row
        End If
    
        For i = 1 To UBound(arHdr)
            sHdr = Trim(arHdr(i, 2))
            If Len(sHdr) > 0 Then ' skip blanks
                Set rng = .Rows(rowHdr).Find(what:=sHdr, LookIn:=xlValues, lookat:=xlWhole)
                If rng Is Nothing Then
                    arHdr(i, 4) = 0
                Else
                    ' store fee column for later gross fee check
                    If arHdr(i, 1) = "Fee" Then iColFee = rng.Column
                    arHdr(i, 4) = rng.Column
                    arHdr(i, 5) = rng.Row
                    lastrow = .Cells(.Rows.Count, rng.Column).End(xlUp).Row
                    arHdr(i, 6) = lastrow - rng.Row - 1 ' data rows
                End If
            Else
                arHdr(i, 4) = 0
            End If
        Next
        
        ' check for mandatory column errors
        Dim sep As String
        For i = 1 To UBound(arHdr)
            If arHdr(i, 3) And arHdr(i, 4) = 0 Then
                msg = msg & sep & arHdr(i, 2)
                sep = ","
            End If
        Next

        If Len(msg) > 0 Then
            .Cells(4, 7) = "The following column labels were not found: "
            .Cells(4, 8) = msg
            .Cells(4, 8).Interior.Color = vbRed
            CheckHeaders = False
            'GoTo Ende
        Else
            .Cells(4, 7).Clear ' G4
            .Cells(4, 8).Clear ' H4
            CheckHeaders = True
        End If
    End With

End Function

' dump array to new workbook to  debug
Sub DumpArray(ar)
    Dim wb As Workbook: Set wb = Workbooks.Add
    With wb.Sheets(1)
       .Name = "arHdr"
       .Range("A1:F1") = Array("Header1", "Header2", "Mandatory", "Column", "Row", "DataRows")
       .Range("A2").Resize(UBound(ar), UBound(ar, 2)) = ar
    End With
    ' save - replace existing
    Application.DisplayAlerts = False
    wb.SaveAs "debug_arHdr.xlsx"
    Application.DisplayAlerts = True
    'wb.Close
End Sub