我有以下(请参见下文)在Excel 2010中一直使用的“文件搜索实用程序”宏。该宏在工作簿的指定文件夹中进行搜索并返回所需的数据(喜欢此宏!)。
在Excel 2010中,搜索(搜索450多个文件)大约需要2分钟,并按照找到的结果显示结果。
在Excel 2016中,搜索花费的时间是原来的两倍多,并且直到宏完全遍历了文件夹中的所有文件之后,才会显示任何结果。
我是中级宏程序员的新手(即我知道足够危险)。调整此代码的任何帮助将不胜感激。
以下是代码:
Option Explicit
Public Sub SearchButton_Click()
Dim astrWorkbooks() As String
Dim strPartNumber As String
Dim strFolderPath As String
Dim vntWorkbooks As Variant
Dim j As Long
On Error GoTo ErrHandler
If Not ValidateData("PartNumber", strPartNumber) Then
MsgBox "Part number has not been entered.", vbExclamation
Exit Sub
End If
If Not ValidateData("SearchFolder", strFolderPath) Then
MsgBox "Search folder has not been entered.", vbExclamation
Exit Sub
End If
Call ClearResultsTable
If Not FolderExists(strFolderPath) Then
MsgBox "Search folder does not exist.", vbExclamation
Exit Sub
End If
vntWorkbooks = GetAllWorkbooks(strFolderPath)
If IsEmpty(vntWorkbooks) Then
MsgBox "Search folder does not contain any Excel workbooks.", vbExclamation
Exit Sub
End If
astrWorkbooks = vntWorkbooks
For j = LBound(astrWorkbooks) To UBound(astrWorkbooks)
Call SearchWorkbook(astrWorkbooks(j), strPartNumber)
Next j
MsgBox "Search has completed. Please check results table.", vbInformation
Exit Sub
ErrHandler:
MsgBox Err.Description, vbExclamation
End Sub
Private Function FolderExists(ByRef strFolderPath As String) As Boolean
On Error GoTo ErrHandler
If Right(strFolderPath, 1) <> Application.PathSeparator Then
strFolderPath = strFolderPath & Application.PathSeparator
End If
FolderExists = (Dir(strFolderPath, vbDirectory) <> "")
Exit Function
ErrHandler:
FolderExists = False
End Function
Private Sub ClearResultsTable()
Dim tblResults As ListObject
Dim objFilter As AutoFilter
Dim rngBody As Range
Set tblResults = wksSearchUtility.ListObjects("Results")
Set objFilter = tblResults.AutoFilter
Set rngBody = tblResults.DataBodyRange
If Not objFilter Is Nothing Then
If objFilter.FilterMode Then
objFilter.ShowAllData
End If
End If
If Not rngBody Is Nothing Then
rngBody.Delete
End If
End Sub
Private Function ValidateData(ByVal strRangeName As String, ByRef strData As String) As Boolean
On Error GoTo ErrHandler
strData = UCase(Trim(wksSearchUtility.Range(strRangeName).Text))
ValidateData = (strData <> vbNullString)
Exit Function
ErrHandler:
ValidateData = False
End Function
Private Function GetAllWorkbooks(strFolderPath As String) As Variant
Dim lngWorkbookCount As Long
Dim astrWorkbooks() As String
Dim strFileName As String
Dim strFilePath As String
On Error GoTo ErrHandler
strFileName = Dir(strFolderPath & "*.xl*")
Do Until (strFileName = vbNullString)
lngWorkbookCount = lngWorkbookCount + 1
strFilePath = strFolderPath & strFileName
ReDim Preserve astrWorkbooks(1 To lngWorkbookCount)
astrWorkbooks(lngWorkbookCount) = strFilePath
strFileName = Dir()
Loop
If lngWorkbookCount > 0 Then
GetAllWorkbooks = astrWorkbooks
Else
GetAllWorkbooks = Empty
End If
Exit Function
ErrHandler:
GetAllWorkbooks = Empty
End Function
Private Sub SearchWorkbook(strFilePath As String, strPartNumber As String)
Dim sht As Worksheet
Dim wbk As Workbook
On Error GoTo ErrHandler
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Set wbk = Workbooks.Open(strFilePath, False)
For Each sht In wbk.Worksheets
Call SearchWorksheet(sht, strPartNumber)
Next sht
ExitProc:
On Error Resume Next
wbk.Close False
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Exit Sub
ErrHandler:
Resume ExitProc
End Sub
Private Sub SearchWorksheet(sht As Worksheet, strPartNumber As String)
Dim rngTableRow As Range
Dim cell As Range
On Error GoTo ErrHandler
For Each cell In Intersect(sht.Columns("B"), sht.UsedRange).Cells
If UCase(cell.Text) Like "*" & strPartNumber & "*" Then
Set rngTableRow = GetNextRow()
rngTableRow.Item(1).Value = sht.Parent.Name
rngTableRow.Item(2).Value = cell.Text
rngTableRow.Item(3).Value = cell.Offset(, -1).Value
rngTableRow.Item(4).Value = cell.Offset(, 6).Value
rngTableRow.Item(5).Value = cell.Offset(, 7).Value
rngTableRow.Item(6) = Range("I3")
End If
Next cell
Exit Sub
ErrHandler:
End Sub
Private Function GetNextRow() As Range
With wksSearchUtility.ListObjects("Results")
If .InsertRowRange Is Nothing Then
Set GetNextRow = .ListRows.Add.Range
Else
Set GetNextRow = .InsertRowRange
End If
End With
End Function
答案 0 :(得分:0)
只想包括OP here提到的解决方案,因为它位于不同的论坛上。
Option Explicit
Public Sub SearchButton_Click()
Dim astrWorkbooks() As String, strPartNumber As String, strFolderPath As String, vntWorkbooks As Variant
Dim j As Long, BlockSize As Long, myRng As Range, BigRng As Range, TempSht As Worksheet, i, myFormula As String, yyy As Range
Dim Drng As Range, SceRng As Range, Destn As Range, msg As String
Application.ScreenUpdating = False
On Error GoTo ErrHandler
If Not ValidateData("PartNumber", strPartNumber) Then
MsgBox "Part number has not been entered.", vbExclamation
Exit Sub
End If
If Not ValidateData("SearchFolder", strFolderPath) Then
MsgBox "Search folder has not been entered.", vbExclamation
Exit Sub
End If
Call ClearResultsTable
If Not FolderExists(strFolderPath) Then
MsgBox "Search folder does not exist.", vbExclamation
Exit Sub
End If
vntWorkbooks = GetAllWorkbooks(strFolderPath)
If IsEmpty(vntWorkbooks) Then
MsgBox "Search folder does not contain any Excel workbooks.", vbExclamation
Exit Sub
End If
Set TempSht = Sheets.Add
astrWorkbooks = vntWorkbooks
BlockSize = 37
For i = 1 To UBound(astrWorkbooks)
myFormula = "='" & strFolderPath & "[" & astrWorkbooks(i) & "]Invoice'!R2C1:R" & BlockSize + 1 & "C9"
Set myRng = TempSht.Range("B" & BlockSize * i - BlockSize + 1).Resize(BlockSize, 9)
myRng.FormulaArray = myFormula
myRng.Offset(, -1).Resize(, 1).Value = astrWorkbooks(i)
myFormula = "='" & strFolderPath & "[" & astrWorkbooks(i) & "]Invoice'!R3C9"
myRng.Columns(myRng.Columns.Count).Offset(, 1).FormulaR1C1 = myFormula
If BigRng Is Nothing Then Set BigRng = myRng Else Set BigRng = Union(BigRng, myRng)
Next i
Set BigRng = BigRng.Offset(, -1).Resize(, BigRng.Columns.Count + 2)
BigRng.Value = BigRng.Value
With TempSht
.Columns("D:H").Delete
.Columns("C:C").Cut
.Columns("B:B").Insert
BigRng.AutoFilter Field:=2, Criteria1:="=*" & strPartNumber & "*"
Set yyy = .AutoFilter.Range
If yyy.Columns(1).SpecialCells(xlCellTypeVisible).Count > 1 Then
Set Drng = .Range("A" & yyy.Rows.Count + 10)
yyy.Offset(1).Resize(yyy.Rows.Count - 1).Copy Drng
Set SceRng = Drng.CurrentRegion
Set Destn = GetNextRow.Resize(SceRng.Rows.Count)
Destn.Value = SceRng.Value
msg = "Search has completed. Please check results table."
Else
msg = "Search has completed. No results found"
End If
Application.DisplayAlerts = False: .Delete: Application.DisplayAlerts = True
End With
Application.ScreenUpdating = True
MsgBox msg, vbInformation
Exit Sub
ErrHandler:
Application.ScreenUpdating = True
MsgBox Err.Description, vbExclamation
End Sub
Private Function FolderExists(ByRef strFolderPath As String) As Boolean
On Error GoTo ErrHandler
If Right(strFolderPath, 1) <> Application.PathSeparator Then
strFolderPath = strFolderPath & Application.PathSeparator
End If
FolderExists = (Dir(strFolderPath, vbDirectory) <> "")
Exit Function
ErrHandler:
FolderExists = False
End Function
Private Sub ClearResultsTable()
Dim tblResults As ListObject
Dim objFilter As AutoFilter
Dim rngBody As Range
Set tblResults = wksSearchUtility.ListObjects("Results")
Set objFilter = tblResults.AutoFilter
Set rngBody = tblResults.DataBodyRange
If Not objFilter Is Nothing Then
If objFilter.FilterMode Then
objFilter.ShowAllData
End If
End If
If Not rngBody Is Nothing Then
rngBody.Delete
End If
End Sub
Private Function ValidateData(ByVal strRangeName As String, ByRef strData As String) As Boolean
On Error GoTo ErrHandler
strData = UCase(Trim(wksSearchUtility.Range(strRangeName).Text))
ValidateData = (strData <> vbNullString)
Exit Function
ErrHandler:
ValidateData = False
End Function
Private Function GetNextRow() As Range
With wksSearchUtility.ListObjects("Results")
If .InsertRowRange Is Nothing Then
Set GetNextRow = .ListRows.Add.Range
Else
Set GetNextRow = .InsertRowRange
End If
End With
End Function
Private Function GetAllWorkbooks(strFolderPath As String) As Variant
Dim lngWorkbookCount As Long
Dim astrWorkbooks() As String
Dim strFileName As String
Dim strFilePath As String
On Error GoTo ErrHandler
strFileName = Dir(strFolderPath & "*.xl*")
Do Until (strFileName = vbNullString)
lngWorkbookCount = lngWorkbookCount + 1
ReDim Preserve astrWorkbooks(1 To lngWorkbookCount)
astrWorkbooks(lngWorkbookCount) = strFileName
strFileName = Dir()
Loop
If lngWorkbookCount > 0 Then
GetAllWorkbooks = astrWorkbooks
Else
GetAllWorkbooks = Empty
End If
Exit Function
ErrHandler:
GetAllWorkbooks = Empty
End Function
答案 1 :(得分:0)
您正在测试B列中的每个单元,这是性能杀手。查看这篇文章,了解如何使用find函数执行此操作,它将更快。
Find all matches in workbook using Excel VBA
该答案中的代码定义了loc
,将.cells
替换为Intersect(sht.Columns("B"), sht.UsedRange)
它应该显示以下内容:
Set Loc = Intersect(sht.Columns("B"), sht.UsedRange).Find(What:="Question?")
显然"Question"
将变成strPartNumber