'How to speed up code to search many sheets

I have active sheet open (Sheet1). I want to pick a value from C column then go through each workbook and within each workbook there are many sheets. If that value (value from C column of active sheet) matches anywhere in that workbook in any sheet then update in active sheet R column saying found otherwise no update.

Sheet1 has 95000 rows.
book3.xlsx has many sheets which totals 34000 rows.
book4.xlsx too has many rows which totals 24000 rows.
Takes around 4 hours to complete.

Option Explicit
Sub Found()
Dim NumberOfValues1, NumberOfValues2, NumberOfValues3 As Integer
Dim i, n, o As Long
Dim wb1, wb2 As Workbook
Dim wbsb1, worksheet1, worksheet2 As Worksheet
Dim newExcel As Excel.Application
Set newExcel = CreateObject("Excel.Application")
Dim value1, value2, value3 As String
    
Set wb1 = Workbooks.Open("C:\commvault2\book3.xlsx")
Set wb2 = Workbooks.Open("C:\commvault2\book4.xlsx")
Application.ScreenUpdating = FALSE 
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
   
NumberOfValues1 = ThisWorkbook.Sheets("Sheet1").Range("A2").End(xlDown).Row
Set wbsb1 = ThisWorkbook.Sheets("Sheet1")

For i = 2 To NumberOfValues1
    value1 = wbsb1.Range("C" & i).Value
    For Each worksheet1 In wb1.Worksheets
        NumberOfValues2 = worksheet1.Range("A1").End(xlDown).Row
        For n = 1 To NumberOfValues2
            value2 = worksheet1.Cells(n, 1)
            If value1 = value2 Then
                wbsb1.Range("R" & i).Value = "Found"
            End If
        Next
    Next
            
    For Each worksheet2 In wb2.Worksheets
        NumberOfValues3 = worksheet2.Range("A1").End(xlDown).Row
        For o = 1 To NumberOfValues3
            value3 = worksheet2.Cells(o, 1)
            If value1 = value3 Then
                wbsb1.Range("R" & i).Value = "Found"
            End If
        Next
    Next
Next 
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True

wb1.Close
wb2.Close
newExcel.Quit
Set newExcel = Nothing
End Sub


Solution 1:[1]

Check Matches

Option Explicit

Sub CheckMatches()
    
    ' Source
    Dim sFolderPath As String: sFolderPath = "C:\commvault2\"
    Const sFileNamesList As String = "Book3.xlsx,Book4.xlsx" ' no spaces!
    Const slCol As String = "A" ' Lookup Column
    Const sfRow As Long = 1 ' suspicious, possibly 2
    ' Destination
    Const dwsName As String = "Sheet1"
    Const dlCol As String = "C" ' Lookup Column
    Const dvCol As String = "R" ' Value Column
    Const dfRow As Long = 2
    Const dvString As String = "Found"
    
    Dim dwb As Workbook: Set dwb = ThisWorkbook ' workbook containing this code
    Dim dws As Worksheet: Set dws = dwb.Worksheets(dwsName)
    
    Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, dlCol).End(xlUp).Row
    If dlRow < dfRow Then Exit Sub ' no data in column
    Dim drCount As Long: drCount = dlRow - dfRow + 1 ' Rows Count
    Dim dlrg As Range: Set dlrg = dws.Cells(dfRow, dlCol).Resize(drCount)
    
    Dim Data As Variant ' Destination Lookup and Value Array
    If drCount = 1 Then
        ReDim Data(1 To 1, 1 To 1): Data = dlrg.Value
    Else
        Data = dlrg.Value
    End If
    
    Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
    dict.CompareMode = vbTextCompare
    
    Dim dKey As Variant
    Dim dr As Long
    For dr = 1 To drCount
        dKey = Data(dr, 1)
        If Not IsError(dKey) Then
            If Len(dKey) > 0 Then
                If Not dict.Exists(dKey) Then
                    Set dict(dKey) = New Collection ' to cover for duplicates
                End If
                dict(dKey).Add dr ' array row numbers
            End If
        End If
    Next dr
    If dict.Count = 0 Then Exit Sub ' only error values or blanks
    
    ReDim Data(1 To drCount, 1 To 1) ' clear destination array
    
    If Right(sFolderPath, 1) <> "\" Then sFolderPath = sFolderPath & "\"
    Dim sFileNames() As String: sFileNames = Split(sFileNamesList, ",")
    Dim nUpper As String: nUpper = UBound(sFileNames)
    
    Application.ScreenUpdating = False
    
    Dim swb As Workbook
    Dim sws As Worksheet
    Dim slrg As Range ' Lookup (Column) Range
    Dim slRow As Long ' Last Row
    Dim srCount As Long ' Rows Count
    Dim sFilePath As String ' File Path
    Dim cItem As Variant ' Collection Item
    Dim n As Long
    Dim FoundAll As Boolean ' No More Dictionary Keys Flag
    
    For n = 0 To nUpper ' loop through workbook names
        sFilePath = sFolderPath & sFileNames(n)
        If Len(Dir(sFilePath)) > 0 Then ' file exists
            Set swb = Workbooks.Open(sFilePath)
            For Each sws In swb.Worksheets
                slRow = sws.Cells(sws.Rows.Count, slCol).End(xlUp).Row
                If slRow >= sfRow Then
                    srCount = slRow - sfRow + 1
                    Set slrg = sws.Cells(sfRow, slCol).Resize(srCount)
                    For Each dKey In dict.Keys ' loop through the dict. keys
                        If IsNumeric(Application.Match(dKey, slrg, 0)) Then
                            ' Loop through each item (row) of the collection
                            For Each cItem In dict(dKey)
                                Data(cItem, 1) = dvString
                            Next cItem
                            dict.Remove dKey
                            If dict.Count = 0 Then
                                FoundAll = True
                                Exit For
                            'Else ' there are still elements in the dictionary
                                'Debug.Print Join(dict.Keys, ",")
                            End If
                        'Else ' match not found
                        End If
                    Next dKey
                    If FoundAll Then Exit For
                'Else ' no data in column
                End If
            Next sws
            swb.Close SaveChanges:=False
        'Else ' file not found
        End If
        If FoundAll Then Exit For
    Next n
          
    ' Write to destination value range ('dvrg').
    Dim dvrg As Range: Set dvrg = dlrg.EntireRow.Columns(dvCol)
    dvrg.Value = Data
    
    Dim msgString As String: msgString = "Matches checked."
    If dict.Count > 0 Then
        msgString = msgString & vbLf & vbLf & _
            "Could not find the following items:" & vbLf & Join(dict.Keys, vbLf)
    End If
    
    Application.ScreenUpdating = True
    
    MsgBox msgString, vbInformation

End Sub

Solution 2:[2]

Using Match():

Sub Found()

    Dim wb As Workbook, c As Range, wbsb1 As Worksheet, ws As Worksheet
    Dim colWB As New Collection, i As Long, v, m
    
    'add workbooks to be searched to the Collection
    colWB.Add Workbooks.Open("C:\commvault2\book3.xlsx")
    colWB.Add Workbooks.Open("C:\commvault2\book4.xlsx")
    '...can add more wb here...
    
    Set wbsb1 = ThisWorkbook.Sheets("Sheet1")
    
    For Each c In wbsb1.Range("C2:C" & wbsb1.Cells(Rows.Count, "C").End(xlUp).Row).Cells
        v = Trim(c.Value)
        If Len(v) > 0 Then
            For Each wb In colWB                  'loop opened workbooks
                For Each ws In wb.Worksheets      'loop sheets
                    m = Application.Match(v, ws.Columns("A"), 0) 'faster than looping or Find()
                    If Not IsError(m) Then        'if got no match `m` would be an error value
                        c.EntireRow.Columns("R").Value = "Found"
                        GoTo nextValue            'an OK use of Goto...
                    End If
                Next ws
            Next wb
        End If
nextValue:
    Next c
    
    'close all opened workbooks
    For i = colWB.Count To 1 Step -1
        Set wb = colWB(i)
        wb.Close False
    Next
      
End Sub

Solution 3:[3]

I am almost sure, the main problem is caused by

NumberOfValues1 = ThisWorkbook.Sheets("Sheet1").Range("A2").End(xlDown).Row

It refers to the last cell/row of the column "A"... Due to this, your code takes all the values in the whole column to perform matching... that means with most empty cells... more than a million empty values... try changing to:

NumberOfValues1 =ThisWorkbook.Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row

the same with NumberOfValues2 and NumberOfValues3

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 VBasic2008
Solution 2 Tim Williams
Solution 3