VBA - Compare Tables on 2 Sheets with Differences

拥有回忆 提交于 2020-02-05 13:58:07

问题


I am new to VBA and was looking for help in writing a sub or code that can compare the same column (B) of two tables on 2 different sheets and combine them into a single table on the first sheet. I have looked at ways to do it and am really confused about using ranges or unions as a solution. I want it to find items both missing from column b in sheet 2 (which will have a dynamic, but known name stored in a variable) and add that entire row to sheet 1 (named 'Dump' with an additional comment on column d, as well as check for rows present in 'Dump' but not present in the other sheet. It only needs to compare column b of the two sheets as column b is the key.

Here's an example of what I am looking for given 2 sheets of data, and the final output.

**Sheet 'Dump'**
+---------------------------+-----+------------------+---+
|             A             |  B  |        C         | D |
+---------------------------+-----+------------------+---+
| v62: Cheetah Mail         | v62 | 206              |   |
| c49: Report Suite         | c49 | appid            |   |
| v75: Message Type         | v75 | NDS Error        |   |
| v42: Core                 | v42 | fd8000d7         |   |
| c37: Message Key          | c37 | fd8000d7         |   |
+---------------------------+-----+------------------+---+

**Sheet 'ICD'**
+---------------------------+-----+-----------+---+
|             A             |  B  |     C     | D |
+---------------------------+-----+-----------+---+
| v62: Cheetah Mail         | v62 | 206       |   |
| c44: Portal               | c44 | polo      |   |
| v75: Message Type         | v75 | NDS Error |   |
| v42: Core                 | v42 | fd8000d7  |   |
| c37: Message Key          | c37 | fd8000d7  |   |
+---------------------------+-----+-----------+---+

Output Sheet 'Dump'
+--------------------+-----+-----------+---------------------------------------+
|         A          |  B  |     C     |                   D                   |
+--------------------+-----+-----------+---------------------------------------+
| v62: Cheetah Mail  | v62 | 206       |                                       |
| c44: Portal        | c44 | polo      | Item found in "ICD" but not in "Dump" |
| c49: Report Suite  | c49 | appid     | Item found in "Dump" but not in "ICD" |
| v75: Message Type  | v75 | NDS Error |                                       |
| v42: Core          | v42 | fd8000d7  |                                       |
| c37: Message Key   | c37 | fd8000d7  |                                       |
+--------------------+-----+-----------+---------------------------------------+

It doesn't matter where the row is placed as it will be sorted later. Thank you so much for the help


回答1:


Here, I got one for you. My code can give right answer for matching two sheet. But the order is not equal with yours. I think that it is no matter what is the order of result row. OK, let check my code:

Public Sub matchRow()

    Dim dumpSheet, icdSheet, outputSheet As Worksheet
    Dim startRow, outputRow, tempDumpRow, tempICDRow, icdRowCount, finishedICDIndex As Integer
    Dim finishedICD() As String
    Dim isExist As Boolean

    'Set sheets
    Set dumpSheet = Sheets("Dump")
    Set icdSheet = Sheets("ICD")
    Set outputSheet = Sheets("Output")

    'Set start row of each sheet for data
    startRow = 1
    outputRow = 1

    'Get row count from ICD sheet
    icdRowCount = icdSheet.Range("A:C").End(xlDown).row

    'Set index
    finishedICDIndex = 0

    'Re-define array
    ReDim finishedICD(0 To icdRowCount - 1)

    'Set the start row
    tempDumpRow = startRow

    'Here I looped with OR state, you can modify it to AND start if you want
    Do While dumpSheet.Range("A" & tempDumpRow) <> "" Or dumpSheet.Range("B" & tempDumpRow) <> "" Or dumpSheet.Range("C" & tempDumpRow) <> ""

        'Reset exist flag
        isExist = False

        'loop all row in ICD sheet
        For tempICDRow = 1 To icdRowCount Step 1

            'If row is not finished for checking.
            If UBound(Filter(finishedICD, tempICDRow)) < 0 Then

                'If all cell are equal
                If dumpSheet.Range("A" & tempDumpRow) = icdSheet.Range("A" & tempICDRow) And _
                   dumpSheet.Range("B" & tempDumpRow) = icdSheet.Range("B" & tempICDRow) And _
                   dumpSheet.Range("C" & tempDumpRow) = icdSheet.Range("C" & tempICDRow) Then

                    'Set true to exist flag
                    isExist = True

                    'Store finished row
                    finishedICD(finishedICDIndex) = tempICDRow

                    finishedICDIndex = finishedICDIndex + 1

                    'exit looping
                    Exit For

                End If

            End If

        Next tempICDRow

        'Show result
        outputSheet.Range("A" & outputRow) = dumpSheet.Range("A" & tempDumpRow)
        outputSheet.Range("B" & outputRow) = dumpSheet.Range("B" & tempDumpRow)
        outputSheet.Range("C" & outputRow) = dumpSheet.Range("C" & tempDumpRow)

        If isExist Then
            outputSheet.Range("D" & outputRow) = ""
        Else
            outputSheet.Range("D" & outputRow) = "Item found in ""Dump"" but not in ""ICD"""
        End If

        'increase output row
        outputRow = outputRow + 1

        'go next row
        tempDumpRow = tempDumpRow + 1

    Loop

    'loop all row in ICD sheet
    For tempICDRow = 1 To icdRowCount Step 1

        'If row is not finished for checking.
        If UBound(Filter(finishedICD, tempICDRow)) < 0 Then

            'Show result
            outputSheet.Range("A" & outputRow) = icdSheet.Range("A" & tempICDRow)
            outputSheet.Range("B" & outputRow) = icdSheet.Range("B" & tempICDRow)
            outputSheet.Range("C" & outputRow) = icdSheet.Range("C" & tempICDRow)
            outputSheet.Range("D" & outputRow) = "Item found in ""ICD"" but not in ""Dump"""

            'increase output row
            outputRow = outputRow + 1

        End If

    Next tempICDRow

End Sub

I guarantee for my answer that it will give the right answer for any data set. Here, my test evidence for my code.

Dump Sheet data:

ICD Sheet data:

This is result:

I know that this answer is not same in order with yours. But I believe that will be helpful for you.




回答2:


The fastest way to compare data existing in two sheets (in case when key exists) is to use ADODB objects. Please, have a look ar example and read comments in code.

Sub CompareDataViaSql()
'declare variables
Dim i As Long, vSheets As Variant, sSql As String
Dim srcWsh As Worksheet, dstWsh As Worksheet
Dim oConn As ADODB.Connection, oRst As ADODB.Recordset

'on error go to error handler
On Error GoTo Err_CompareDataViaSql

'add destination sheet
Set dstWsh = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count))
dstWsh.Name = "ResultList_" & Format(Now, "yyyyMMddHHss")

'define collection of sheets to loop through
vSheets = Array("Dump", "ICD")

'loop through the collection of sheets
'build sql command
For i = LBound(vSheets) To UBound(vSheets)
    Set srcWsh = ThisWorkbook.Worksheets(vSheets(i))
    sSql = sSql & "SELECT [F1], [F2], [F3], '" & srcWsh.Name & "' AS [F4]" & vbCr & _
        "FROM [" & srcWsh.Name & "$" & Replace(srcWsh.UsedRange.Address, "$", "") & "]" & vbCr & _
        "UNION ALL" & vbCr
Next i

'remove last UNION ALL command
sSql = Left(sSql, Len(sSql) - 10)
'continue building sql command
'in this case - pivot table
sSql = "TRANSFORM COUNT(T.[F2])" & vbCr & _
       "SELECT T.[F1], T.[F2], T.[F3]" & vbCr & _
        "FROM (" & sSql & ") AS T" & vbCr & _
        "GROUP BY T.[F1], T.[F2], T.[F3]" & vbCr & _
        "PIVOT(T.[F4])"

'create new adodb connection
Set oConn = New ADODB.Connection
With oConn
    'define connection string
    .ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";" & _
            "Extended Properties='Excel 12.0 Macro;HDR=NO';"
    'open connection
    .Open
End With

'create new adodb recordset
Set oRst = New ADODB.Recordset
'open recordset
oRst.Open sSql, oConn, adOpenStatic, adLockReadOnly

'add headers
For i = 0 To oRst.Fields.Count - 1
    dstWsh.Range("A1").Offset(ColumnOffset:=i) = oRst.Fields(i).Name
Next
i = i - 1
With dstWsh.Range("A1:" & dstWsh.Range("A1").Offset(ColumnOffset:=i).Address)
    .Font.Bold = True
    .Font.Color = vbRed
    .Interior.Color = vbYellow
End With

'define destination row
i = 2
'copy data from recordset
dstWsh.Range("A" & i).CopyFromRecordset oRst
'fit columns width
dstWsh.UsedRange.Columns.AutoFit

'clean up
Exit_CompareDataViaSql:
    On Error Resume Next
    oRst.Close
    Set oRst = Nothing
    oConn.Close
    Set oConn = Nothing
    Set srcWsh = Nothing
    Set dstWsh = Nothing
    Exit Sub

'error handler
Err_CompareDataViaSql:
    MsgBox Err.Description, vbExclamation, Err.Number
    Resume Exit_CompareDataViaSql

End Sub

Result:

 F1                          F2      F3         Dump    ICD
 c37: Message Key            c37     fd8000d7   1       1
 c44: Portal                 c44     polo               1
 c49: Report Suite           c49     appid      1   
 v42: Core                   v42     fd8000d7   1       1
 v62: Cheetah Mail           v62     206        1       1
 v75: Message Type           v75     NDS Error  1       1

Note: This isn't exactly what you want, but... Assuming that 1 means data exists on list and null means data does not exists: c44 exists only on ICD list and c49 exists only on Dump list.

For further information, please see: TRANSFORM (MS Access)



来源:https://stackoverflow.com/questions/31667675/vba-compare-tables-on-2-sheets-with-differences

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!