Excel VBA Workbook opens with data in scientific data type - excel

I have an Excel VBA code that extracts data from different files, one is a .csv while the other is an .xls file. These 2 files are both of varying file name and path. The problem I am facing now is that when the files opens as a Workbook, the data are already in scientific data type. This sudden change in data type causes errors during extraction and may even lead to wrong data interpretation.
Sub ExtractData()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim SourceFile As Variant
Dim SourceWB As Workbook
Dim wsRs As Worksheet
Dim PTDate As Date, SODate As Date
Dim ProcSteps As Range
Set wsRs = ThisWorkbook.Sheets("References")
wsRs.Activate
Set ProcSteps = wsRs.Range(Cells(2, 1), Cells(2, 1).End(xlDown))
Range("M:M, P:P,AA:AA").ColumnWidth = 25
'--------------get prod trackout data--------------
SourceFile = Application.GetOpenFilename(Title:="Please select Production TrackOut File ('FwWeb0101')", Filefilter:="Text Files(*.csv),csv*") 'get filepath
If SourceFile \<\> False Then
Set SourceWB = Application.Workbooks.Open(SourceFile)
Range("A:J").ColumnWidth = 25
Range("A:B,D:D,F:H,K:M,O:R").Delete Shift:=xlToLeft
Range(Cells(1, 1), Cells(1, 1).End(xlToRight).End(xlDown)).AutoFilter Field:=1, Criteria1:=Split(Join(Application.Transpose(ProcSteps), ","), ","), Operator:=xlFilterValues
Range(Cells(1, 1), Cells(1, 1).End(xlToRight).End(xlDown)).Copy Destination:=wsRs.Cells(1, 10)
SourceWB.Close
'--------------get step output report data--------------
SourceFile = Application.GetOpenFilename(Title:="Please select B800 Step Output Report File ('basenameFwCal0025')", Filefilter:="Excel Files(.xls),*xls*") 'get filepath
If SourceFile \<\> False Then
Set SourceWB = Application.Workbooks.Open(SourceFile)
Range("B:B,D:D,K:N,P:R").Delete Shift:=xlToLeft
With ActiveSheet.Sort
.SortFields.Clear
.SortFields.Add2 Key:=Columns("B"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SortFields.Add2 Key:=Columns("A"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SetRange Columns("A:J")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'-------------------------copy all lots-----------------
Range(Cells(1, 1), Cells(1, 1).End(xlToRight).End(xlDown)).AutoFilter Field:=2, Criteria1:=Split(Join(Application.Transpose(ProcSteps), ","), ","), Operator:=xlFilterValues
Range(Cells(1, 1), Cells(1, 1).End(xlToRight).End(xlDown)).Copy Destination:=wsRs.Cells(1, 16)
SourceWB.Close
'------------------------check workweek----------------
Else: MsgBox "No B800 Step Output Report file was selected.", vbCritical ' no file selected
With wsRs.Columns("J:N")
.Clear
.ColumnWidth = 8.11
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.DisplayStatusBar = True
Exit Sub
End If
Else: MsgBox "No Production TrackOut file was selected.", vbCritical ' no file selected
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.DisplayStatusBar = True
Exit Sub
End If
ThisWorkbook.Save
End Sub
Thank you for the help.
Cheers!
I tried to open the files using the File > Open option of Excel, this gives me the Text to Columns Option. I tried the Delimiter but with no selected option but the file still opens with scientific data type.

In order to oblige Excel showing the (whole) number instead of existing scientific format, you can set a custom NumberFormat equal to the respective number number of digits.
If column 7 ("G:G") is the column you intend changing the format and the numeric value has 10 digits, you should simple use:
Columns(7).NumberFormat = "0000000000"
If the numeric values in the respective column may have different number of digits, you can proceed in the next way:
Sub changeNumform()
Dim ws As Worksheet, lastR As Long, i As Long
Const colNo As Long = 7
Set ws = ActiveSheet
lastR = ws.cells(ws.rows.count, colNo).End(xlUp).Row
ws.Columns(colNo).EntireColumn.AutoFit: Stop
For i = 2 To lastR
ApplyNumFormat ws.cells(i, colNo)
Next i
End Sub
Sub ApplyNumFormat(c As Range)
Dim lenNo As Long
If InStr(c.Text, "E+") > 0 Then 'if in Scientific Format
lenNo = Len(Split(c.Text, ".")(0)) + CLng(Split(c.Text, "E+")(1))
c.NumberFormat = String(lenNo, "0")
End If
End Sub

Related

How to speed up vba code that delete rows when column Q has blank cells

I have a sheet of almost 100000 rows & column A to Q
I have a code that delete entire rows if column Q has blank cells.
I have tried this code on 4000 rows it is running in 3 minutes but when I take 100000 rows it just processing for hours.
I will be very great full if some help/guide me in speeding up this code.
The code is :
Sub DeleteBlank()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim lo As ListObject
set lo = sheets("BOM 6061").ListObjects(1)
Sheets("BOM 6061").Activate
lo.AutoFilter.ShowAllData
lo.range.AutoFilter Field:=17, Criteria1:=""
Application.DisplayAlerts = False
Application.Calculation = xlCalculationAutomatic
lo.DataBodyRange.SpecialCells(xlCellsTypeVisible).Delete
Application.DisplayAlerts = True
lo.AutoFilter.ShowAllData
End Sub
Remove Criteria Rows in an Excel Table Efficiently
In a nutshell, if you don't sort the criteria column, deleting the rows may take 'forever'.
The following will do just that, keeping the initial order of the remaining rows.
Option Explicit
Sub DeleteBlankRows()
Const wsName As String = "BOM 6061"
Const tblIndex As Variant = 1
Const CriteriaColumnNumber As Long = 17
Const Criteria As String = ""
' Reference the table.
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim tbl As ListObject: Set tbl = ws.ListObjects(tblIndex)
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' Remove any filters.
If tbl.ShowAutoFilter Then
If tbl.AutoFilter.FilterMode Then tbl.AutoFilter.ShowAllData
Else
tbl.ShowAutoFilter = True
End If
' Add a helper column and write an ascending integer sequence to it.
Dim lc As ListColumn: Set lc = tbl.ListColumns.Add
lc.DataBodyRange.Value = _
ws.Evaluate("ROW(1:" & lc.DataBodyRange.Rows.Count & ")")
' Sort the criteria column ascending.
With tbl.Sort
.SortFields.Clear
.SortFields.Add2 tbl.ListColumns(CriteriaColumnNumber).Range, _
Order:=xlAscending
.Header = xlYes
.Apply
End With
' AutoFilter.
tbl.Range.AutoFilter Field:=CriteriaColumnNumber, Criteria1:=Criteria
' Reference the filtered (visible) range.
Dim svrg As Range
On Error Resume Next
Set svrg = tbl.DataBodyRange.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
' Remove the filter.
tbl.AutoFilter.ShowAllData
' Delete the referenced filtered (visible) range.
If Not svrg Is Nothing Then svrg.Delete
' Sort the helper column ascending.
With tbl.Sort
.SortFields.Clear
.SortFields.Add2 lc.Range, Order:=xlAscending
.Header = xlYes
.Apply
.SortFields.Clear
End With
' Delete the helper column.
lc.Delete
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
' Inform.
MsgBox "Blanks deleted.", vbInformation
End Sub
I would not use an Autofilter on large data sets as they can take quite a bit of time trying to enumerate the available options before actually filtering the data. The AutoFilter.ShowAllData takes just as much time. For my super simple test dataset, which consisted of 26 columns of 1000000 rows, it took 30+ seconds for each to process.
From what I can tell you are filtering the list to show only the blank items and then deleting the blank rows. Since the filtering is what is causing the delay we could just loop through each row looking at a specific column and if it is blank you can just delete it. Below is an example of how to do this.
**Edit: After testing I found this to be much slower than what you would want. Check out the next example below as it is super fast.
Option Explicit
Sub DeleteBlank()
Application.ScreenUpdating = False
Dim calcType As Integer
Dim rowCount, columnNumToCheck, currow, dataStartRow As Long
Dim WkSht As String
Dim lo As ListObject
WkSht = "BOM 6061" 'The name of the worksheet where the table is located.
columnNumToCheck = 17 'The column number to check for blank cells.
calcType = Application.Calculation
Application.Calculation = xlCalculationManual
Set lo = Sheets(WkSht).ListObjects(1)
rowCount = lo.ListRows.Count
dataStartRow = (lo.DataBodyRange.Row - 1)
For currow = rowCount To 1 Step -1
If Sheets(WkSht).Cells((currow + dataStartRow), columnNumToCheck).Value = "" Then
Call DeleteRows(WkSht, (currow + dataStartRow))
End If
Next currow
Application.Calculation = calcType
Application.ScreenUpdating = True
End Sub
Private Sub DeleteRows(sheetNameIn As String, startRow As Long, Optional optionalEndRow As Long)
If IsNull(optionalEndRow) Or optionalEndRow = 0 Then
optionalEndRow = startRow
End If
Worksheets(sheetNameIn).Range(startRow & ":" & optionalEndRow).Delete Shift:=xlUp
End Sub
If you are able to sort your data where the blank cells are all together you could use the below to perform a single delete function remove them all at once. This deleted 70000 rows in a few seconds.
Sub DeleteBlankWithSort()
'Application.ScreenUpdating = False
Dim columnNumToCheck, tableLastRow, lrow As Long
Dim calcType As Integer
Dim WkSht As String
Dim lo As ListObject
WkSht = "BOM 6061" 'The name of the worksheet where the table is located.
columnNumToCheck = 17 'The column number to check for blank cells.
calcType = Application.Calculation
Application.Calculation = xlCalculationManual
Set lo = Sheets(WkSht).ListObjects(1)
tableLastRow = FindLastRow(WkSht, (columnNumToCheck))
With lo.Sort
.SortFields.Clear
.SortFields.Add _
Key:=Range("Table1[[#All],[q]]"), _
SortOn:=xlSortOnValues, _
Order:=xlDescending, _
DataOption:=xlSortNormal
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
lrow = FindLastRow(WkSht, (columnNumToCheck), (tableLastRow))
Call DeleteRows(WkSht, (tableLastRow), (lrow + 1))
Application.Calculation = calcType
Application.ScreenUpdating = True
End Sub
Private Sub DeleteRows(sheetNameIn As String, startRow As Long, Optional optionalEndRow As Long)
If IsNull(optionalEndRow) Or optionalEndRow = 0 Then
optionalEndRow = startRow
End If
Worksheets(sheetNameIn).Range(startRow & ":" & optionalEndRow).Delete Shift:=xlUp
End Sub
Private Function FindLastRow(sheetNameIn As String, columnNum As Long, Optional optionalStartRow As Long) As Long
'finds the last row of the column passed in the sheetname passed in
If IsNull(optionalStartRow) Or optionalStartRow = 0 Then
optionalStartRow = 1048576
End If
FindLastRow = Worksheets(sheetNameIn).Range(Cells(optionalStartRow, columnNum).Address).End(xlUp).Row
End Function
I had an simple example of this from a while ago. Advanced filtering is the fastest way to filter in place or to filter and copy in excel/vba. In advanced filtering you usually have your filters listed out in columns/rows and can have as many as you need, use >"" for filtering out blanks on a column, should take no time at all. In my example it might be different as this was used alongside sheetchange to autofilter if anything was added to the filters.
Sub Advanced_Filtering_ModV2()
Dim rc As Long, crc As Long, trc As Long
Dim wb As Workbook, ws As Worksheet
Set wb = ActiveWorkbook: Set ws = wb.Worksheets("sheet1")
ws.Range("AA1").Value = ws.Range("Q1").Value: ws.Range("AA2").Value = ">"""""
On Error Resume Next
ws.ShowAllData: rc = ws.Range("A" & Rows.Count).End(xlUp).Row
ws.Range("A1:V" & rc).AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=ws.Range("AA1:AA2")
On Error GoTo 0
End Sub

VBA: Split sheet on certain rule

I need help with VBA which will split current sheet Test1 depending values from A rows.
Test1 sheet is in format:
Now i need to split sheet Test1 into two (or more) sheets which will contains all rows which begins with 1.1 and 1.4 (this values will be same rule, but different numbers).
So after run VBA code, it will be created sheet Test1-1 (green region) containing all data which starts with 1.1:
1.1
1.1.1
1.1.2
1.1.3
And second sheet Test1-2 (red region) which starts with 1.4:
1.4
1.4.1
1.4.2
After creation origin Test1 sheet can be removed.
Can you please give me help or guide i don't have any clue/idea to achieve this.
With the below code the output will be:
Two Sheets:
Test1-1
Test1-4
If you want to get this output:
Test1-1
Test1-2
You should:
Sort data based on the first column
Create another variable with initial value 1 and every time that Sheetname change value instead of use Sheetname variable , use the new variable.
Guidlines for:
Sorting:
Option Explicit
Sub Sort()
Dim LR As Long
With ThisWorkbook.Worksheets("Test1")
LR = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
ThisWorkbook.Worksheets("Test1").Sort.SortFields.Clear
ThisWorkbook.Worksheets("Test1").Sort.SortFields.Add2 Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Test1").Sort
.SetRange Range("A2:D" & LR)
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
New Variable
From: ActiveWorkbook.Worksheets("Test1-" & SheetName)
To: ActiveWorkbook.Worksheets("Test1-" & NewVariable)
Try:
Option Explicit
Sub test()
Dim LR As Long
Dim LRN As Long
Dim i As Long
Dim SheetName As String
Dim wsTest As Worksheet
Dim wsNew As Worksheet
With ThisWorkbook.Worksheets("Test1")
LR = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
For i = LR To 1 Step -1
With ThisWorkbook.Worksheets("Test1")
SheetName = Mid(.Range("A" & i), InStr(1, .Range("A" & i).Value, ".") + 1, 1)
End With
Set wsTest = Nothing
On Error Resume Next
Set wsTest = ActiveWorkbook.Worksheets("Test1-" & SheetName)
On Error GoTo 0
If wsTest Is Nothing Then
Worksheets.Add.Name = "Test1-" & SheetName
End If
With ActiveWorkbook.Worksheets("Test1-" & SheetName)
LRN = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
ThisWorkbook.Worksheets("Test1").Range("A" & i & ":D" & i).Cut ActiveWorkbook.Worksheets("Test1-" & SheetName).Range("A" & LRN + 1)
Next i
Application.DisplayAlerts = False
ThisWorkbook.Worksheets("Test1").Delete
Application.DisplayAlerts = True
End Sub

Excel VBA to duplicate and fill the default template based on number of rows

I have a default template and need to populate the value in A column (Material) of the output sheet from column I of the source template. I created a macro which duplicates the number of output row based on number of parts in source template. The issue here is the part number is populated only in the first column and its not looping to the other blank rows.
Source Template
Sample Output sheet
Result:
VBA Code:
Sub Process_File()
Dim Src_File As Workbook
Dim Out_Template As Workbook
Dim Src_Tot_Row, Out_Tot_Row As Integer
Dim REG_CODE
REG_CODE = "C299"
Set Src_File = Workbooks.Open("C:\Users\raja\Desktop\NPI Automation\Sadhan\Source_Data.xlsx") 'Read source file name
Set Out_Template = Workbooks.Open("C:\Users\raja\Desktop\NPI Automation\Sadhan\Plant\AMS.xlsx") 'Read output template file name
'------------------------------------------------------------------- Portion-2
' Workbooks.Open (Sheet1.Range("G7").Value) ' Open source excel file
Src_File.Sheets("Input_sheet").Activate
If Range("I7").Value <> "Part numbers" Then ' Checking correct input file
MsgBox "Select correct source file.!"
End
End If
Range("I8").Select
Selection.End(xlDown).Select
Src_Tot_Row = ActiveCell.Row
'------------------------------------------------------------------- Portion-3
' Workbooks.Open (Sheet1.Range("G9").Value) ' Open output template excel file
Out_Template.Sheets("Plant").Activate 'Find Total Rows in Output Template
Range("B1").Select
Selection.End(xlDown).Select
Out_Tot_Row = ActiveCell.Row
Dim Temp_Row_Calc As Integer
Temp_Row_Calc = Src_Tot_Row - 7
Temp_Row_Calc = (Out_Tot_Row - 2) * Temp_Row_Calc ' Calculate total rows for data duplicate
Range("A2:AJ" & Out_Tot_Row).Copy
Range("A" & Out_Tot_Row + 1 & ":AJ" & Temp_Row_Calc + 2).PasteSpecial xlPasteValues
'------------------------------------------------------------------- Portion-4
Range("A1").EntireColumn.Insert ' Inserting temporary column for sorting back
Range("A1").Value = "1"
Range("A" & Temp_Row_Calc - 1).Select
Temp_Row_Calc = Temp_Row_Calc - 1
Range(Selection, Selection.End(xlUp)).Select
Selection.DataSeries Rowcol:=xlColumns, Type:=xlLinear, Date:=xlDay, _
Step:=1, Stop:=Temp_Row_Calc, Trend:=False
If ActiveSheet.AutoFilterMode = False Then ' Check Filter Mode and apply
ActiveSheet.Range("A1").AutoFilter
End If
ActiveSheet.AutoFilter.Sort.SortFields.Clear
ActiveSheet.AutoFilter.Sort.SortFields.Add Key:=Range( _
"C1:C" & Temp_Row_Calc), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("Plant").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
For I = 2 To Temp_Row_Calc
If Range("C" & I).Value = REG_CODE Then
Src_File.Sheets("Input_Sheet").Activate 'Activate Source Excel
ReDim ary(1 To Src_Tot_Row - 1) ' Copy material numbers
For j = 1 To Src_Tot_Row - 1
ary(j) = Src_File.Sheets("Input_Sheet").Cells(j + 1, 1)
Next j
Range("I8:I" & Src_Tot_Row).Copy 'Copy source part numbers
Out_Template.Sheets("Plant").Activate 'Activate Out Template Excel
Range("B" & I).SpecialCells(xlCellTypeVisible).PasteSpecial (xlPasteValues)
ActiveSheet.AutoFilter.Sort.SortFields.Clear
ActiveSheet.AutoFilter.Sort.SortFields.Add Key:=Range( _
"A1:A" & Temp_Row_Calc), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("Plant").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'filtervalues = """8121-0837"", ""B5L47-67901"", ""B5L47-67903"", "" ="""
ary(Src_Tot_Row - 7) = ""
ActiveSheet.Range("$A$1:$AJ$" & Temp_Row_Calc).AutoFilter Field:=2, Criteria1:=ary, Operator:=xlFilterValues
Dim cl As Range, rng As Range
Set rng = Range("A2:A" & Temp_Row_Calc)
For Each cl In rng
If cl.EntireRow.Hidden = False Then 'Use Hidden property to check if filtered or not
If cl <> "" Then
x = cl
Else
cl.Value = x
End If
End If
Next
Exit For
End If
Next I
If ActiveSheet.AutoFilterMode Then ' Check Filter Mode and apply
ActiveSheet.Range("A1").AutoFilter
End If
Columns(1).EntireColumn.Delete
MsgBox "Completed!"
'-------------------------------------------------------------------
End Sub
Function GetFilenameFromPath(ByVal strPath As String) As String
If Right$(strPath, 1) <> "\" And Len(strPath) > 0 Then
GetFilenameFromPath = GetFilenameFromPath(Left$(strPath, Len(strPath) - 1)) + Right$(strPath, 1)
End If
End Function
Sub Test()
Range("A1").Value = "1"
Range("A" & Out_Tot_Row).Select
Range(Selection, Selection.End(xlUp)).Select
Selection.DataSeries Rowcol:=xlColumns, Type:=xlLinear, Date:=xlDay, _
Step:=1, Stop:=Out_Tot_Row, Trend:=False
End Sub
Your code has several errors, suggest to Step Into it using [F8] and the Locals Window then you will be able to see/learn what each line of the code is doing and apply necessary correction. Besides that, to have your code looping through all rows remove this line Exit For near the end of the Process_File procedure.
It seems that your objective is to duplicate all records in the worksheet Plant times the number of Part Numbers in worksheet Input_sheet, assigning to each record in the worksheet Plant each of the Part Numbers in worksheet Input_sheet. If this is correct then try this code:
Solution:
This code assumes the following:
The Part Numbers are continuous (no blank cells in between)
The Data in worksheet Plant is continuous, starting at A1 and contains a header row.
.
Rem The following two lines must be at the top of the VBA Module
Option Explicit
Option Base 1
Sub Process_File()
Dim wbkSrc As Workbook, wbkTrg As Workbook
Dim wshSrc As Worksheet, wshTrg As Worksheet
Dim aPrtNbr As Variant, aData As Variant
Dim lItm As Long, lRow As Long
Rem Application Settings OFF
With Application
.EnableEvents = False
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
Rem Set Source Worksheet
On Error Resume Next
Set wbkSrc = Workbooks.Open("C:\Users\raja\Desktop\NPI Automation\Sadhan\Source_Data.xlsx")
Set wshSrc = wbkSrc.Worksheets("Input_sheet")
If wshSrc Is Nothing Then GoTo ExitTkn
Rem Set Target Worksheet
Set wbkTrg = Workbooks.Open("C:\Users\raja\Desktop\NPI Automation\Sadhan\Plant\AMS.xlsx")
Set wshTrg = wbkTrg.Worksheets("Plant")
If wshTrg Is Nothing Then GoTo ExitTkn
Rem Application Settings OFF
Application.DisplayAlerts = False
With wshSrc.Range("I7")
If .Value2 <> "Part numbers" Then
Rem Validate Input Worksheet
MsgBox "Select correct source file!", vbSystemModal + vbCritical
GoTo ExitTkn
Else
Rem Set Part Number Array
aPrtNbr = .Offset(1).Resize(-.Row + .End(xlDown).Row).Value2
aPrtNbr = WorksheetFunction.Transpose(aPrtNbr)
End If: End With
Rem Set Data Array
With wshTrg.Cells(1).CurrentRegion
aData = .Offset(1).Resize(-1 + .Rows.Count).Value2
End With
Rem Duplicate Data and Assign Part Numbers
With wshTrg
For lItm = 1 To UBound(aPrtNbr)
lRow = lRow + IIf(lItm = 1, 2, UBound(aData))
With .Cells(lRow, 1).Resize(UBound(aData), UBound(aData, 2))
.Value = aData
.Columns(1).Value = aPrtNbr(lItm)
End With: Next: End With
ExitTkn:
Rem Application Settings OFF
With Application
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
.DisplayAlerts = True
End With
End Sub
Suggest to read the following pages to gain a deeper understanding of the resources used:
Option keyword,
On Error Statement,
With Statement,
Using Arrays,
WorksheetFunction Object (Excel),
For...Next Statement,
Range Object (Excel),
Range.CurrentRegion Property (Excel),
Range.Offset Property (Excel)

Macro Running Out Of Memory When Run Twice

I am new to this forum but have been reading a large number of posts recently as I am currently self teaching VBA for use at work!
I currently am having an issue with a bit of code that I have created. The aim of the code is to autofilter multiple sheets depending on a cell value that is double clicked on, it then copies these filtered results to another "Master Report" sheet. The issue is that it runs perfectly fine once, after which if I try to run it again or any of my other macro's within the workbook an error pops up asking me to close things to free up memory!
I have tried running the macro once, saving and closing the workbook (to clear anything that might be cached), re-opening and running and yet the same error persists. I also tried changing my .select prompts with .activate as suggested by:
How to avoid running out of memory when running VBA
but that seemed to break my code... then again I may have just implemented it wrong as I am a bit of a VBA noob Can anyone help me optimize my code to prevent this?
my code is as below:
Private Sub Merge()
With Selection
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
End With
Selection.Merge
End Sub
-------------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Cancel = True
Application.ScreenUpdating = False
Application.EnableEvents = False
Sheets("Master Report").Cells.Delete 'clear old master report
Column = Target.Column
Row = Target.Row
'this automatically filters information for a single part and creates a new master report with summary information
PartNumber = Cells(Row, 2).Value 'capture target part number for filtering
PartDesc = Cells(Row, 7).Value 'capture target part description
PartNumberWildCard = "*" & PartNumber & "*" 'add wildcards to allow for additional terms
With Worksheets("NCR's") 'filter NCR sheet
.Select
On Error Resume Next
ActiveSheet.ShowAllData 'remove any previous filters
On Error GoTo 0
.Range("A1").AutoFilter Field:=2, Criteria1:=PartNumberWildCard
End With
Sheets("NCR's").Select
Sheets("NCR's").Range("A3:K3").Select
Sheets("NCR's").Range(Selection, Selection.End(xlDown)).Select 'select NCR filtered summary info
Selection.Copy
Sheets("Master Report").Select
Sheets("Master Report").Range("A1").Formula = PartNumber
Sheets("Master Report").Range("D1").Formula = PartDesc 'Print part no. & description at top of master report
Sheets("Master Report").Range("A4").Select
ActiveSheet.Paste 'paste filtered NCR info into master report
Sheets("Master Report").Range("A3:K3").Select
Call Merge
ActiveCell.FormulaR1C1 = "NCR's"
With Worksheets("CR's") 'filter CR sheet
.Select
On Error Resume Next
ActiveSheet.ShowAllData 'remove any previous filters
On Error GoTo 0
.Range("A1").AutoFilter Field:=3, Criteria1:=PartNumberWildCard
End With
Sheets("CR's").Select
Sheets("CR's").Range("A7:F7").Select
Sheets("CR's").Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("Master Report").Select
Sheets("Master Report").Range("P4").Select
ActiveSheet.Paste
Sheets("Master Report").Range("RP3:U3").Select
Call Merge
ActiveCell.FormulaR1C1 = "CR's"
With Worksheets("PO's") 'filter PO sheet
.Select
On Error Resume Next
ActiveSheet.ShowAllData 'remove any previous filters
On Error GoTo 0
.Range("A1").AutoFilter Field:=2, Criteria1:=PartNumberWildCard
End With
Sheets("PO's").Select
Sheets("PO's").Range("A3:H3").Select
Sheets("PO's").Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("Master Report").Select
lastRow = Sheets("Master Report").Range("A" & Rows.Count).End(xlUp).Row
lastRow = lastRow + 3
Sheets("Master Report").Range("A" & lastRow).Select
ActiveSheet.Paste
Sheets("Master Report").Range("A" & lastRow - 1 & ":H" & lastRow - 1).Select
Call Merge
ActiveCell.FormulaR1C1 = "PO's"
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Another piece of info that may help is that I tried removing the last of the three filter/copy/paste routines, this allowed me to run the code about 3 times before running into the same memory error. Also the Debugger always gets stuck on the command to clear the master report at the beginning of the macro
Sheets("Master Report").Cells.Delete 'clear old master report
There are a couple of tips to speed up your macro and make it use less memory (less selecting, copying pasting). For a start it would be better to loop through your sheets rather than one long script for every one.
Dim arrShts As Variant, arrSht As Variant
arrShts = Array("NCR's", "CR's", "PO's")
For Each arrSht In arrShts
Worksheets(arrSht).Activate
'rest of your code'
Next arrSht
In the array add any other sheets you need to run the script on
Declaring variables is recommended also:
Dim masterws As Worksheet
Set masterws = Sheets("Master Report")
masterws.Activate
masterws.Range("A1").Formula = PartNumber
I haven't been able to do this 100% accurately, but you could limit your code down to something like the following
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Cancel = True
Application.ScreenUpdating = False
Application.EnableEvents = False
Column = Target.Column
Row = Target.Row
PartNumber = Cells(Row, 2).Value 'capture target part number for filtering
PartDesc = Cells(Row, 7).Value 'capture target part description
PartNumberWildCard = "*" & PartNumber & "*" 'add wildcards to allow for additional terms
Dim arrShts As Variant, arrSht As Variant, lastrw As Integer
Dim masterws As Worksheet
Set masterws = Sheets("Master Report")
masterws.Cells.Clear 'clear old master report
arrShts = Array("NCR's", "CR's", "PO's")
For Each arrSht In arrShts
Worksheets(arrSht).Activate
lastrw = Sheets(arrSht).Range("K" & Rows.Count).End(xlUp).Row
With Worksheets(arrSht) 'filter NCR sheet
On Error Resume Next
ActiveSheet.ShowAllData 'remove any previous filters
On Error GoTo 0
.Range("A1").AutoFilter Field:=2, Criteria1:=PartNumberWildCard
End With
Range(Cells(3, 1), Cells(lastrw, 11)).Copy
lastRow = Sheets("Master Report").Range("A" & Rows.Count).End(xlUp).Row
masterws.Activate
masterws.Range("A1").Formula = PartNumber
masterws.Range("D1").Formula = PartDesc 'Print part no. & description at top of master report
masterws.Range("A" & lastRow).PasteSpecial xlPasteValues
masterws.Range("A" & lastRow - 1 & ":H" & lastRow - 1).Select
Call Merge
ActiveCell.FormulaR1C1 = arrSht
Application.CutCopyMode = False
Next arrSht
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
This is in no way complete, and will edit as I find bits, but a good place to start to reduce the strain of your macro.
try this refactoring of your code
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, cancel As Boolean)
Dim iRow As Long
Dim PartNumber As String, PartDesc As String, PartNumberWildCard As String
Dim masterSht As Worksheet
Set masterSht = Worksheets("Master Report")
cancel = True
iRow = Target.Row
PartNumber = Cells(iRow, 2).Value 'capture target part number for filtering
PartDesc = Cells(iRow, 7).Value 'capture target part description
PartNumberWildCard = "*" & PartNumber & "*" 'add wildcards to allow for additional terms
'clear old master report and write headers
With masterSht
.Cells.ClearContents
.Cells.UnMerge
.Range("A1").Value = PartNumber
.Range("D1").Value = PartDesc 'Print part no. & description at top of master report
FilterAndPaste "NCR's", "K1", 2, PartNumberWildCard, .Range("A4")
FilterAndPaste "CR's", "F1", 3, PartNumberWildCard, .Range("P4")
FilterAndPaste "PO's", "H1", 2, PartNumberWildCard, .Cells(rows.count, "A").End(xlUp).Offset(3)
End With
End Sub
Sub FilterAndPaste(shtName As String, lastHeaderAddress As String, fieldToFilter As Long, criteria As String, targetCell As Range)
With Worksheets(shtName)
.AutoFilterMode = False 'remove any previous filters
With .Range(lastHeaderAddress, .Cells(.rows.count, 1).End(xlUp))
.AutoFilter Field:=fieldToFilter, Criteria1:=criteria
If Application.WorksheetFunction.Subtotal(103, .Resize(, 1)) > 1 Then
.Resize(.rows.count - 1).Offset(1).SpecialCells(XlCellType.xlCellTypeVisible).Copy Destination:=targetCell
With targetCell.Offset(-1).Resize(, .Columns.count)
Merge .Cells
.Value = shtName
End With
End If
End With
End With
End Sub
Private Sub Merge(rng As Range)
With rng
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.Merge
End With
End Sub
should it work for you, as it did in my tests, then I can add you some info, if you care about

Code to remove 'NULL' values

Let me give a quick layout what our process is:
I export a report into Excel (Let's call this workbook "Raw Data"). I run an Extract macro on the imported file:
Sub Extract_Sort_1601_January()
'
Dim ANS As Long
ANS = MsgBox("Is the January 2016 Swivel Master File checked out of SharePoint and currently open on this desktop?", vbYesNo + vbQuestion + vbDefaultButton1, "Master File Open")
If ANS = vbNo Or IsWBOpen("Swivel - Master - January 2016") = False Then
MsgBox "The required workbook is not currently open. Please open the correct file and restart the Extract process. This procedure will now terminate.", vbOKOnly + vbExclamation, "Terminate Procedure"
Exit Sub
End If
Cells.EntireRow.Hidden = False
Application.ScreenUpdating = False
' This line autofits the columns C, D, O, and P
Range("C:C,D:D,O:O,P:P").Columns.AutoFit
Dim LR As Long
For LR = Range("B" & Rows.Count).End(xlUp).Row To 2 Step -1
If Range("B" & LR).Value <> "1" Then
Rows(LR).EntireRow.Delete
End If
Next LR
With ActiveWorkbook.Worksheets("Extract").Sort
With .SortFields
.Clear
.Add Key:=Range("B2:B2000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Add Key:=Range("D2:D2000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Add Key:=Range("O2:O2000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Add Key:=Range("J2:J2000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Add Key:=Range("K2:K2000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Add Key:=Range("L2:L2000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
End With
.SetRange Range("A2:Z2000")
.Apply
End With
Cells.WrapText = False
Sheets("Extract").Range("A2").Select
Dim LastRow As Integer, i As Integer, erow As Integer
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
For i = 2 To LastRow
If Cells(i, 2) = "1" Then
' As opposed to selecting the cells, this will copy them directly
Range(Cells(i, 1), Cells(i, 26)).Copy
' As opposed to "Activating" the workbook, and selecting the sheet, this will paste the cells directly
With Workbooks("Swivel - Master - January 2016.xlsm").Sheets("Swivel")
erow = .Cells(.Rows.Count, 1).End(xlUp).Offset(1, 0).Row
.Cells(erow, 1).PasteSpecial xlPasteAll
End With
Application.CutCopyMode = False
End If
Next i
Application.ScreenUpdating = True
End Sub
This copies data from the "extract" file into another workbook (This workbook is called "Swivel"). This part completes successfully. Once this is completed, in the "Swivel" workbook, we then run a remove duplicates macro:
Sub Remove_Duplicates()
'
Application.ScreenUpdating = False
ActiveSheet.Range("$A$1:$Z$2000").RemoveDuplicates Columns:=Array(10, 11, 12, 13, 14, 15, 16), Header:=xlYes
ActiveWindow.SmallScroll Down:=6
Range("C" & Rows.Count).End(xlUp).Offset(1).Select
Application.ScreenUpdating = True
End Sub
Somewhere between the copying of data into the 'Swivel' workbook and running the Remove Duplicates macro, there is a null value (I think) inserted into the cells in column AD in the rows just pasted in. I only know this because this code is running in the worksheet for changes:
Private Sub Worksheet_Change(ByVal Target As Range)
'
Dim r As Range
Set r = Target.EntireRow
If Target.row = 1 Then Exit Sub ' Don’t change header color
If r.Cells(1, "AD").Value <> "" Then
r.Font.Color = RGB(0, 176, 80)
Else
r.Font.ColorIndex = 1
End If
End Sub
For clarification, here is where the above subs reside:
Extract_Sort_1601_January is part of an Add-in I created for the "raw data" file.
Remove_Duplicates is in a module in the "Swivel" workbook.
WorkSheet_Change is in the Sheet1 object in the "Swivel" workbook.
Data from the reporting site is exported to the "raw data" workbook
Extract_Sort_1601_January copies data into the existing "Swivel"
workbook (In this case that workbook name is "Swivel - Master -
January 2016.xlsm")
Remove_Duplicates is initiated on the "Swivel" workbook.
If there is no data in column AD of the "Swivel" workbook, the text in that row should be black. However, that is not the case after running the Remove Duplicates macro, the text is green. If I go to the 'empty' cell (column AD) in that row and click delete, then the row changes to black text. I also checked to see if there is a space in the cell, but there is not. How do I code the removal of this 'null' value that is making the Worksheet Change sub believe there is a value in the cell? And, can this be added to the 'Remove Duplicates' sub?
Thanks for all the assistance!
We extract the file from an internal site. It was brought to my attention that the reporting team had changed their preferences in their instance of the reporting tool to export files using Excel XP/2003 version settings. All the code I have compiled was using the same report, but in 2007 and newer formatting. Once this change in preferences was changed for the reporting team to use 2007 and newer for the exports, this issue was corrected. So in the end, the code was fine and there was no ghost. This proves that communication and Change Management are excellent tools. Thanks to everyone who tried to help figure this one out. All your effort is greatly appreciated.
test this code:
Sub test()
Dim LastRow As Long
dim i as long
LastRow = 100 'change this to the last row (if it work)
Application.EnableEvents = True
For i = 2 To LastRow
If Trim(Range("AD" & i).Value) = "" Then Range("AD" & i).ClearContents
Next
End Sub
The problem was that there are lots of "fake empty" cells in the worksheet. I have not been able to figure out where these came from, but I found this code and integrated it into the Remove_Duplicates sub to ClearContents:
Sub Remove_Duplicates()
'
Application.ScreenUpdating = False
Dim usedrng As Range
ActiveSheet.Range("$A$1:$Z$2000").RemoveDuplicates Columns:=Array(10, 11, 12, 13, 14, 15, 16), Header:=xlYes
For Each usedrng In ActiveSheet.UsedRange
If usedrng.Value = "" Then
usedrng.ClearContents
End If
Next
Range("C" & Rows.Count).End(xlUp).Offset(1).Select
Application.ScreenUpdating = True
End Sub
So now, this code works as intended:
Private Sub Worksheet_Change(ByVal Target As Range)
'
Dim r As Range
Set r = Target.EntireRow
If Target.row = 1 Then Exit Sub ' Don’t change header color
If r.Cells(1, "AD").Value <> "" Then
r.Font.Color = RGB(0, 176, 80)
Else
r.Font.ColorIndex = 1
End If
End Sub
Thanks to everyone who helped get me to this point.

Resources