VBA to remove named ranges in batches from excel workbook - excel

Sometimes our workbooks at work get so overloaded with named ranges, which we don't even use, that the tool we normally use to remove names, or even the name manager, will no longer function. I did some digging around here and after finding this post: VBA Remove 100k + named ranges, I started using the below code:
Sub dlname()
Dim j As Long
For j = 20000 To 1 Step -1
If j <= ActiveWorkbook.Names.Count Then
ActiveWorkbook.Names(j).Delete
End If
Next j
ActiveWorkbook.Save
End Sub
For the most part this gets the job done (very slowly) however it periodically just stops working, and I'd prefer for this to be done on a loop until the job is done with the workbook being saved every time. If I use code that doesn't try and do the job in chunks then I just get a memory error so I'm pretty sure it needs to be done piece meal.
Sorry I am not a coder so I'm unsure how to update. Any help would be appreciated.
Thanks,

I don't see anything really "wrong" with your code - it could be tidied up a bit, but the essential process is the same:
'remove all names from activeworkbook
Sub RemoveNames()
With ActiveWorkbook.Names
Do While .Count > 0
.Item(1).Delete
Loop
End With
End Sub
'create a lot of names for testing...
Sub AddNames()
Dim i As Long
For i = 1 To 10000
ActiveWorkbook.Names.Add "Test_" & Format(i, "0000000"), ActiveSheet.Cells(i, 1)
Next i
End Sub

The process of deleting UNUSED names can be complicated. This is an example of searching through all the defined names in a workbook and deleting ONLY those NOT USED in a formula.
The bit at the top and bottom of the routine will greatly speed up the process...
Option Explicit
Sub DeleteAllUnusedNames()
'--- disable all interactions for SPEED
With Application
.EnableEvents = False
.DisplayAlerts = False
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
Dim totalNames As Long
Dim namesDeleted As Long
Dim definedName As Variant
For Each definedName In ThisWorkbook.names
Dim nameIsUsed As Boolean
nameIsUsed = True
totalNames = totalNames + 1
Dim sheet As Worksheet
For Each sheet In ThisWorkbook.Sheets
If Not NameIsInFormula(definedName.name, sheet) Then
nameIsUsed = False
Exit For
End If
Next sheet
If Not nameIsUsed Then
namesDeleted = namesDeleted + 1
definedName.Delete
End If
Next definedName
Debug.Print totalNames & " names found, " & namesDeleted & " deleted"
'--- re-enable all interactions
With Application
.EnableEvents = True
.DisplayAlerts = True
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
End With
End Sub
Public Function NameIsInFormula(ByVal thisName As String, _
ByRef thisSheet As Worksheet) As Boolean
On Error Resume Next
Dim cellsWithFormulas As Range
Set cellsWithFormulas = thisSheet.Cells.SpecialCells(xlCellTypeFormulas)
If cellsWithFormulas Is Nothing Then
NameIsInFormula = False
Exit Function
End If
On Error GoTo 0
Dim cellsFound As Range
Set cellsFound = cellsWithFormulas.Find(What:=thisName, LookIn:=xlFormulas, _
LookAt:=xlPart, MatchCase:=False, _
SearchFormat:=False)
'--- optional if you want to see where it is...
' If Not cellsFound Is Nothing Then
' Debug.Print vbTab & thisName & " found in " & _
' thisSheet.name & "!" & cellsFound.Address
' End If
NameIsInFormula = (Not cellsFound Is Nothing)
End Function

Related

VBA Auto Filter If Criteria Exists

I've recorded macros to autofilter and delete rows from a table. But this is not dynamic in the sense that if the filter criteria does not exist in a given table then the macro will break.
I am trying to create a code that will autofilter and delete the rows if the the criteria exists or otherwise do nothing. I am trying to follow this post, but I am missing something. Please help.
My code returns no errors, but also does not do anything. I added the message box to make sure that it was actually running.
Here is my code so far:
Sub autofilter()
Dim lo As ListObject
Set lo = Worksheets("BPL").ListObjects("Table1")
With Sheets(1)
If .AutoFilterMode = True And .FilterMode = True Then
If lo.Parent.autofilter.Filters(7).Criteria1 = "APGFORK" Then
'
lo.Range.autofilter Field:=7, Criteria1:="APGFORK"
Application.DisplayAlerts = False
lo.DataBodyRange.SpecialCells(xlCellTypeVisible).Delete
Application.DisplayAlerts = True
lo.autofilter.ShowAllData
'
End If
End If
End With
MsgBox ("Code Complete")
End Sub
Delete Filtered Rows in an Excel Table
Not entire rows!
Option Explicit
Sub DeleteFilteredRows()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim tbl As ListObject: Set tbl = wb.Worksheets("BPL").ListObjects("Table1")
Dim dvrg As Range ' Data Visible Range
With tbl
If .ShowAutoFilter Then
If .Autofilter.FilterMode Then .Autofilter.ShowAllData
End If
.Range.Autofilter 7, "APGFORK"
On Error Resume Next
Set dvrg = tbl.DataBodyRange.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
.Autofilter.ShowAllData
End With
Dim IsSuccess As Boolean
If Not dvrg Is Nothing Then
dvrg.Delete xlShiftUp
IsSuccess = True
End If
If IsSuccess Then
MsgBox "Data deleted.", vbInformation
Else
MsgBox "Nothing deleted.", vbExclamation
End If
End Sub
I don't know if it is a bug or a feature, but .AutoFilterMode seems to returns False all the time in Excel 2013 or later. All examples I see that use .AutoFilterMode are earlier than that.
I think the replacement is .ShowAutoFilter on the listobject. In your code, lo.ShowAutoFilter should return True or False depending on whether or not the autofilter is set or not.
But the rest of your code seems problematic too. The test
If lo.Parent.autofilter.Filters(7).Criteria1 = "APGFORK" Then
throws an error and removes the autofilter.
I Ended up taking a different approach:
Dim LastRowG As Long
LastRowG = Range("G" & Rows.Count).End(xlUp).Row
For i = 2 To LastRowG
If Range("G" & i).Value = "APGFORK" Then
lo.Range.autofilter Field:=7, Criteria1:="APGFORK"
Application.DisplayAlerts = False
lo.DataBodyRange.SpecialCells(xlCellTypeVisible).Delete
Application.DisplayAlerts = True
lo.autofilter.ShowAllData
Else
End If
Next i
This way if "APGFORK" does not exist in a data set, it will move on without an error code.
Try this code
Sub Test()
Call DelFilterParam("BPL", "Table1", 7, "APGFORK")
End Sub
Sub DelFilterParam(ByVal wsName As String, ByVal stTable As String, ByVal iField As Integer, ByVal vCriteria As Variant)
Dim x As Long, y As Long, z As Long
With ThisWorkbook.Worksheets(wsName)
With .ListObjects(stTable).DataBodyRange
x = .Rows.Count: y = .Columns.Count
.AutoFilter
.AutoFilter Field:=iField, Criteria1:=vCriteria
On Error Resume Next
z = .SpecialCells(xlCellTypeVisible).Count
On Error GoTo 0
If (x * y) > z And z <> 0 Then .EntireRow.Delete
.AutoFilter
End With
End With
End Sub

Why are deleted rows reappearing in an excel table when the filter in removed in VBA?

The VBA code I have has worked perfectly on two other machines and with several other worksheets without the data reappearing. I've created a macro that takes a master spreadsheet and creates a new spreadsheet for each school listed in the table. I just got a new laptop and installed Excel 365 on it. I copied the VBA code to the new machine, but when I ran it, each new worksheet still contained the data for all the schools, not just the school for that particular file. I stepped through the code, and the schools did delete, but when it got to the part where the filter was removed from the table ws.ListObjects("Data").AutoFilter.ShowAllData, all the deleted rows reappeared. I'm stumped on why this is happening - It didn't happen on the other two machines and other iterations of the file that I've used this macro on. I don't know if it's an Excel setting or a setting on this particular master file. The other two machines - one used Excel 365, and the other Excel 2016. The data is not part of PowerPivot and is not a PowerQuery, so the data only lives in the table in the worksheet.
Here is the macro:
Dim i As Integer, wb As Workbook, schools() As Variant, schools_to_delete() As Variant
Dim ws As Worksheet, rng As Range, dt As String
schools = SchoolsInList()
dt = MonthName(Month(Now)) & " " & Year(Now)
Set wb = ActiveWorkbook
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
For i = 1 To UBound(schools)
wb.SaveCopyAs ("Galileo " & dt & " " & schools(i) & ".xlsx")
Workbooks.Open ("Galileo " & dt & " " & schools(i) & ".xlsx")
Workbooks("Galileo " & dt & " " & schools(i) & ".xlsx").Activate
Set ws = Sheets("Data")
ws.Activate
schools_to_delete = schools
schools_to_delete(i) = "x"
Set rng = ws.ListObjects("Data").DataBodyRange
With ws
.AutoFilterMode = False
ws.ListObjects("Data").Range.AutoFilter Field:=18, Criteria1:= _
Array(schools_to_delete), Operator:=xlFilterValues
ws.Range(rng.Address).SpecialCells(xlCellTypeVisible).Delete
.AutoFilterMode = False
ws.ListObjects("Data").AutoFilter.ShowAllData
End With
ActiveWorkbook.RefreshAll
Call SelectA1
ActiveWorkbook.Save
ActiveWorkbook.Close
Next i
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.DisplayAlerts = True
End Sub
Function SchoolsInList() As Variant
Dim schools() As String
Dim C As Collection
Dim r As Range
Dim i As Long
Dim last_row As Long
last_row = Cells(Rows.Count, 1).End(xlUp).Row
Set C = New Collection
On Error Resume Next
For Each r In Worksheets("Data").Range("R2:R" & last_row).Cells
C.Add r.Value, CStr(r.Value)
Next
On Error GoTo 0
ReDim A(1 To C.Count)
For i = 1 To C.Count
A(i) = C.Item(i)
Next i
SchoolsInList = A
End Function
Sub SelectA1()
Dim i As Long
For i = 1 To ActiveWorkbook.Sheets.Count
Sheets(i).Activate
ActiveWindow.ScrollColumn = 1
ActiveWindow.ScrollRow = 1
Range("A1").Select
Next i
ActiveWorkbook.Worksheets(2).Activate
End Sub
I found the problem - the .AutoFilterMode = False didn't actually clear the filters that had already been placed on the table in question. The visible data WAS deleted, but the data that was filtered before the macro was run remained, and when the ws.ListObjects("Data").AutoFilter.ShowAllData ran, it cleared the previous filter, showing the rows that had been filtered before. I added the .ShowAllData code to the beginning of the With statement to avoid the same problem at a future date.

How to Recreate a Sheet and Keep References Valid?

I have a client who is hand holding a bunch of worksheets that should be standardized. They are created from importing CSV files. Basically, I need to replace the current manual sheets while they are being referenced from another tab without breaking the current references.
I've reduced the problem to a single workbook with 2 sheets. Sheet1 cell A1 references Sheet2 cell A1 which holds the string "Sheet2A1CellData"
Everything commented out below has been tried including Application.Volatile and Application.Calculation.
Option Explicit
Sub TestSheet2Delete()
Dim TmpSheet2 As Worksheet: Set TmpSheet2 = Sheets("Sheet2")
'Application.Volatile
If TmpSheet2 Is Nothing Then
Exit Sub
End If
'Application.Calculation = False
Application.DisplayAlerts = False
TmpSheet2.Delete
Application.DisplayAlerts = True
Set TmpSheet2 = Worksheets.Add(After:=Sheets("Sheet1"))
If TmpSheet2 Is Nothing Then
Exit Sub
End If
TmpSheet2.Name = "Sheet2"
TmpSheet2.Range("A1").Value = "Sheet2A1CellData"
'Application.Calculation = True
End Sub
Sheet1 A1 was originally =Sheet2!A1. When I run the function above from the VBE, Sheet1 cell A1 is set to =#REF!A1.
How can I keep the reference valid after the sheet has been replaced?
Obviously, the real world problem is much larger and re-importing CSV data requires updating 132,000 cells. 6000 rows x 22 Columns.
Thanks for any help.
Thank you presenting a real good question.
First of all disclaimer: This is not an direct solution but and workaround we had to adopt years back.
Exactly similar problem problem had been encountered in my workplace (literally made us to pull out our hairs), and we also tried to go for iNDIRECT. But since the formulas in the working sheets are complex we failed to replace them with INDIRECT. So instead of lengthy manual replacement of the hundreds of Formulas in the working sheet, we used to insert a temp Sheet and change the formulas reference to that sheet. After importing new sheet and renaming it as old sheet's name, formulas were reverted back to original.
I tried to reproduce the code used (since I don't have access to same files now). We only used the Sub ChangeFormulas, Here I used the same in line with your code.
Option Explicit
Sub TestSheet2Delete()
Dim Wb As Workbook
Dim Ws As Worksheet, Ws1 As Worksheet, Ws2 As Worksheet
Dim Xstr As String, Ystr As String
Set Wb = ThisWorkbook
Set Ws = Wb.Sheets("Sheet1")
Xstr = "Sheet2"
Ystr = "TempSheetX"
Set Ws1 = Wb.Sheets(Xstr)
Set Ws2 = Worksheets.Add(After:=Ws)
Ws2.Name = Ystr
DoEvents
ChangeFormulas Ws, Xstr, Ystr
Application.DisplayAlerts = False
Ws1.Delete
' Now again add another sheet with Old name and change formulas back to Original
Set Ws1 = Worksheets.Add(After:=Ws)
Ws1.Name = Xstr
DoEvents
ChangeFormulas Ws, Ystr, Xstr
Ws2.Delete
Application.DisplayAlerts = True
End Sub
Sub ChangeFormulas(Ws As Worksheet, Xstr As String, Ystr As String)
Dim Rng As Range, C As Range, FirstAddress As String
Set Rng = Ws.UsedRange
With Rng
Set C = .Find(What:=Xstr, LookIn:=xlFormulas)
If Not C Is Nothing Then
FirstAddress = C.Address
Do
C.Formula = Replace(C.Formula, Xstr, Ystr)
Set C = .FindNext(C)
If C Is Nothing Then Exit Do
If C.Address = FirstAddress Then Exit Do
Loop
End If
End With
End Sub
Another simplest workaround is not to delete the Sheet at all and import the CSV and copy the full sheet onto the sheet in question. However This fully depends on actual working conditions involving CSV and all.
AFTER I posted (of course :-)), this link came up on the right: Preserve references that recommends using INDIRECT. I have now changed Sheet1 A1 to =INDIRECT("Sheet2!"&"A1").
I am not certain why the named ranges suggested in the link are needed. The indirect call above seems to work without a named range.
If this works in the larger project, I will mark this as complete.
My original answer did not work for non-contiguous cells. However, I really like the Range to Variants and then back to Range pattern. Very fast. So I rewrote my original answer into more reusable code that tests using non-contiguous cells.
Function PreserveFormulaeInVariantArr(ByVal aWorksheet As Worksheet, _
ByVal aIsNoFormulaErr As Boolean, _
ByRef aErrStr As String) As Variant
Dim TmpRange As Range
Dim TmpAreaCnt As Long
Dim TmpVarArr As Variant
Dim TmpAreaVarArr As Variant
PreserveFormulaeInVariantArr = Empty
If aWorksheet Is Nothing Then
aErrStr = "PreserveFormulaeInVariantArr: Worksheet is Nothing."
Exit Function
End If
Err.Clear
On Error Resume Next
Set TmpRange = aWorksheet.Cells.SpecialCells(xlCellTypeFormulas)
If Err.Number <> 0 Then 'No Formulae.
PreserveFormulaeInVariantArr = Empty
If aIsNoFormulaErr Then
aErrStr = "PreserveFormulaeInVariantArr: No cells with formulae."
End If
Exit Function
End If
TmpAreaVarArr = Empty
On Error GoTo ErrLabel
ReDim TmpVarArr(1 To TmpRange.Areas.Count, 1 To 2)
For TmpAreaCnt = LBound(TmpVarArr) To UBound(TmpVarArr)
TmpVarArr(TmpAreaCnt, 1) = TmpRange.Areas(TmpAreaCnt).Address 'Set 1st Element to Range
TmpAreaVarArr = TmpRange.Areas(TmpAreaCnt).Formula 'Left TmpArrVarArr for Debugging
TmpVarArr(TmpAreaCnt, 2) = TmpAreaVarArr 'Creates Jagged Array
Next TmpAreaCnt
PreserveFormulaeInVariantArr = TmpVarArr
Exit Function
ErrLabel:
aErrStr = "PreserveFormulaeInVariantArr - Error Number: " + CStr(Err.Number) + " Error Description: " + Err.Description
End Function
Function RestoreFormulaeFromVariantArr(ByVal aWorksheet As Worksheet, _
ByVal aIsEmptyAreaVarArrError As Boolean, _
ByVal aAreaVarArr As Variant, _
ByRef aErrStr As String) As Boolean
Dim TmpVarArrCnt As Long
Dim TmpRange As Range
Dim TmpDim1Var As Variant
Dim TmpDim2Var As Variant
Dim TmpDim2Cnt As Long
Dim TmpDim2UBound As Long
RestoreFormulaeFromVariantArr = False
On Error GoTo ErrLabel
If aWorksheet Is Nothing Then
Exit Function
End If
If IsEmpty(aAreaVarArr) Then
If aIsEmptyAreaVarArrError Then
aErrStr = "RestoreFormulaeFromVariantArr: Empty array passed."
Else
RestoreFormulaeFromVariantArr = True
End If
Exit Function
End If
For TmpVarArrCnt = 1 To UBound(aAreaVarArr)
TmpDim1Var = aAreaVarArr(TmpVarArrCnt, 1) 'This is always the range.
TmpDim2Var = aAreaVarArr(TmpVarArrCnt, 2) 'This can be a Variant or Variant Array
aWorksheet.Range(TmpDim1Var).Formula = TmpDim2Var
Next TmpVarArrCnt
RestoreFormulaeFromVariantArr = True
Exit Function
ErrLabel:
aErrStr = "PreserveFormulaeInVariantArr - Error Number: " + CStr(Err.Number) + " Error Description: " + Err.Description
End Function
Sub TestPreserveFormulaeInVariantArr()
Dim TmpPreserveFormulaeArr As Variant
Dim TmpErrStr As String
Dim TmpIsNoFormulaErr As Boolean: TmpIsNoFormulaErr = True 'Change If Desired
Dim TmpEmptySheet1 As Boolean: TmpEmptySheet1 = False 'Change If Desired
Dim TmpSheet1 As Worksheet: Set TmpSheet1 = Sheets("Sheet1")
Dim TmpSheet2 As Worksheet
Err.Clear
On Error Resume Next
Set TmpSheet2 = Sheets("Sheet2")
On Error GoTo 0
'Always Delete Sheet2
If (TmpSheet2 Is Nothing) = False Then
Application.DisplayAlerts = False
TmpSheet2.Delete
Application.DisplayAlerts = True
Set TmpSheet2 = Nothing
End If
If TmpSheet2 Is Nothing Then
Set TmpSheet2 = Worksheets.Add(After:=Sheets("Sheet1"))
TmpSheet2.Name = "Sheet2"
TmpSheet2.Range("A1") = "Sheet2A1"
TmpSheet2.Range("B1") = "Sheet2A1"
TmpSheet2.Range("C4") = "Sheet2C4"
If TmpEmptySheet1 Then
TmpSheet1.Cells.ClearContents
Else
TmpSheet1.Range("A1").Formula = "=Sheet2!A1"
TmpSheet1.Range("B1").Formula = "=Sheet2!B1"
TmpSheet1.Range("C4").Formula = "=Sheet2!C4"
End If
End If
TmpPreserveFormulaeArr = PreserveFormulaeInVariantArr(TmpSheet1, TmpIsNoFormulaErr, TmpErrStr)
If TmpErrStr <> "" Then
MsgBox TmpErrStr
Exit Sub
End If
'Break Formulae and Cause #Ref Violation
Application.DisplayAlerts = False
TmpSheet2.Delete
Application.DisplayAlerts = True
'Add Sheet2 Back
Set TmpSheet2 = Worksheets.Add(After:=Sheets("Sheet1"))
TmpSheet2.Name = "Sheet2"
TmpSheet2.Range("A1") = "Sheet2A1"
TmpSheet2.Range("B1") = "Sheet2A1"
TmpSheet2.Range("C4") = "Sheet2C4"
'Restore Formulas Back to Sheet1
If RestoreFormulaeFromVariantArr(TmpSheet1, TmpIsNoFormulaErr, TmpPreserveFormulaeArr, TmpErrStr) = False Then
MsgBox TmpErrStr
Exit Sub
End If
End Sub
The TestPreserveFormulaeInVariantArr can be run in the VBE with options to set empty values. Any comments appreciated.

Excel VBA - open a workbook and pasting data

I found this excellent code however I need to adapt it for my purposes.
Firstly I need to open a data workbook that is on our network. The problem I have is that it is likely at times to be open by another user and will offer the option of "read only". How can I get it to accept the read-only option so that I can commence extracting the data.
Secondly it copies using the "=" . How can I change it to copy just the values?
First macro:
Sub test()
'to open another workbook
Application.ScreenUpdating = False
Workbooks.Open Filename:=ThisWorkbook.Path & "\Schedule.xls"
ThisWorkbook.Activate
Application.ScreenUpdating = True
End Sub
2nd Macro:
Dim Sh As Worksheet
Dim Newsh As Worksheet
Dim myCell As Range
Dim ColNum As Integer
Dim RwNum As Long
Dim Basebook As Workbook
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
'Delete the sheet "Summary-Sheet" if it exist
Application.DisplayAlerts = False
On Error Resume Next
ThisWorkbook.Worksheets("Summary-Sheet").Delete
On Error GoTo 0
Application.DisplayAlerts = True
'Add a worksheet with the name "Summary-Sheet"
Set Basebook = ThisWorkbook
Set Newsh = Basebook.Worksheets.Add
Newsh.Name = "Summary-Sheet"
'The links to the first sheet will start in row 2
RwNum = 1
For Each Sh In Basebook.Worksheets
If Sh.Name <> Newsh.Name And Sh.Visible Then
ColNum = 1
RwNum = RwNum + 1
'Copy the sheet name in the A column
Newsh.Cells(RwNum, 1).Value = Sh.Name
For Each myCell In Sh.Range("A1,D5:E5,Z10") '<--Change the range
ColNum = ColNum + 1
Newsh.Cells(RwNum, ColNum).Formula = _
"='" & Sh.Name & "'!" & myCell.Address(False, False)
Next myCell
End If
Next Sh
Newsh.UsedRange.Columns.AutoFit
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
You could always open the workbook as read-only if you are only extracting data.
Instead of using .formula use .value

Excel VLOOKUP using String to look for data in a File. Goes too slow

This is the best I can get. Incase anyone searches and needs the best answer for this type of data pull. I had to break it down into sections; these work computers just can't handle this type of load. Max data pull is around 800 lines and takes around a minute to pull all the formula(s) and data. Thanks to the people below with thier help.
Sub Update()
Dim ScreenUpdateState As Boolean
Dim StatusBarShow As Boolean
Dim CalcState As Long
Dim EventState As Boolean
Dim ws As Worksheet
Dim location_string As String
Dim count As Integer
'Save the current state of Excel settings
ScreenUpdateState = Application.ScreenUpdating
StatusBarShow = Application.DisplayStatusBar
CalcState = Application.Calculation
EventState = Application.EnableEvents
'Change Excel to faster procedure settings
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Set ws = ThisWorkbook.Sheets("%")
location_string = Sheets("Driver(s)").Cells(5, "G").Text
For count = 7 To 139
Cells(count, "F").Formula = "=IFERROR((VLOOKUP($C" & count & ",'S:\xxxx\xxxxx\xxxxxx\xxxxx\xxxxxxxxxx\[xxxxxxxxxxxxxxxxxxxxxxxxxx.xlsx]" + location_string + "'!$A:$K,11,FALSE)),"" - "")"
Next count
'Restore Excel settings to original state
Application.ScreenUpdating = ScreenUpdateState
Application.DisplayStatusBar = StatusBarShow
Application.Calculation = CalcState
Application.EnableEvents = EventState
MsgBox ("Update Complete")
End Sub
Good luck!
Ross
Orignal thread:
Ok I have this now and it works. It however is to slow to be used as
this one code only runs maybe 1/16th of the required calculations and
takes a few minutes to complete. Anyone know a way to speed up the
process?
Sub Test()
Dim ws As Worksheet
Dim location_string As String
Dim count As Integer
Set ws = ThisWorkbook.Sheets("%")
location_string = ws.Cells(2, "E").Text
count = 7
While count < 138
Cells(count, "F").Formula = "=IFERROR((VLOOKUP($C" & count & ",
'S:\xxxx\xxxx\xxxx\xxxx\xxxxx\[xxxxxx.xlsx]" + location_string + "'!$A:$K,11,FALSE)),"" - "")"
count = count + 1
Wend
MsgBox ("Done")
End Sub
Below is the original post:
I have a list of values on another sheet that will create part of the
string I need:
=CONCATENATE ((INDEX('Driver(s)'!$B$1:$B$48,'Driver(s)'!$G$3,1)),"Epic")
this will set a cell to = 'O614Epic
now trying to add a Vlookup to pull from:
S:\xxxxxxxxxxxxxxx\xxxxxxxxx\xx\xx\xx\[Random File Name.xlsx]0614Epic'!$A:$K
Based on the drop down box, the ####Epic file will change to the
correct value as a string at the moment but can not get Vlookup to
pull from the correct workbook. I also need this to open non-opened
workbooks. Too much data to import into the Excel workbook itself.
Thanks.
Ross
If there's no getting around using VLOOKUP, skip to the Bonus Info. Instead of having VLOOKUP formulas recalculating with every change and slowing down your spreadsheet, you can use VBA to find and put the value in the cell instead of a formula. I did my best to tailor it to what you provided. Please let me know if you have questions on any parts.
Function WorksheetExists(ByVal WorksheetName As String) As Boolean
Dim Sht As Worksheet
For Each Sht In ActiveWorkbook.Worksheets
If Application.Proper(Sht.Name) = Application.Proper(WorksheetName) Then
WorksheetExists = True
Exit Function
End If
Next Sht
End Function
Sub RossQuestion()
Dim wbdata As Workbook
Dim ws As Worksheet
Dim Cell As Range
Dim location_string As String
Dim strcheck As String
Dim count As Integer
Set ws = ThisWorkbook.Sheets("%")
location_string = ws.Cells(2, "E").Text
count = 7
While count < 138
Set wbdata = Workbooks.Open("S:\xxxx\xxxx\xxxx\xxxx\xxxxx\xxxxxx.xlsx", , True)
If WorksheetExists(location_string) Then
Set Cell = wbdata.Sheets(location_string).Columns("A").Find(ws.Range("$C$" & count).Value, _
wbdata.Sheets(location_string).Range("A1"), xlFormulas, xlWhole, xlByRows, xlNext, False)
strcheck = Cell.Offset(0, 10).Value
If Len(Trim(strcheck)) <> 0 Then
ws.Cells(count, "F").Value = Cell.Offset(0, 10).Value
Else
ws.Cells(count, "F").Value = " - "
End If
Else
ws.Cells(count, "F").Value = " - "
End If
count = count + 1
wbdata.Close False
Wend
MsgBox "Done"
End Sub
Bonus Info:
If you're not wrapping your code in something like this, consider using this for all future VBA. The 1st tip in this link details these actions.
Dim ScreenUpdateState As Boolean
Dim StatusBarShow As Boolean
Dim CalcState As Long
Dim EventState As Boolean
'Save the current state of Excel settings
ScreenUpdateState = Application.ScreenUpdating
StatusBarShow = Application.DisplayStatusBar
CalcState = Application.Calculation
EventState = Application.EnableEvents
'Change Excel to faster procedure settings
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
'<<<YOUR CODE HERE>>>
'Restore Excel settings to original state
Application.ScreenUpdating = ScreenUpdateState
Application.DisplayStatusBar = StatusBarShow
Application.Calculation = CalcState
Application.EnableEvents = EventState
Original Answer:
While you can reference data from other workbooks (even non-opened), your path in VLOOKUP's table_array argument has to be completely typed in.
So while VLOOKUP accepts...
=VLOOKUP('Driver(s)'!$G$3, 'S:\xxxxx\FileName.xlsx'!$A:$K, 3, FALSE)
it won't accept any calculations or concatenations in the table_array such as...
=VLOOKUP('Driver(s)'!$G$3, 'S:\xxxxx\ & O614Epic & .xlsx'!$A:$K, 3, FALSE)
=VLOOKUP('Driver(s)'!$G$3, 'S:\xxxxx\ & INDIRECT(B1) & Epic.xlsx'!$A:$K, 3, FALSE)
Anything other than the complete path string is considered too volatile. Same goes for MATCH INDEX. Unfortunately VLOOKUP isn't as dynamic as you'd like and ####Epic needs to be typed as O614Epic by you and not coming from another cell.
There's always VBA. Everything's possible with VBA.
Remove the loop and try this:
Sub Test()
Dim ws As Worksheet
Dim location_string As String
Dim myformula As String
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With
Set ws = ThisWorkbook.Sheets("%")
location_string = ws.Cells(2, "E").Value '~~> I'd suggest you use Value
myformula = "=IFERROR((VLOOKUP($C7,'S:\xxxx\xxxx\xxxx\xxxx\xxxxx\[xxxxxx.xlsx]" & _
location_string & "'!$A:$K,11,FALSE)),"" - "")"
Range("F7:F138").Formula = myformula
Msgbox "Done"
With Application
.ScreenUpdating = True
.DisplayAlerts = True
End With
End Sub
This takes 5 sec in my machine but it will differ specially if the target file is in a network server. HTH.

Resources