I have code that runs and does what I want it to do with the click of the command button, however when executing, it runs very slow. The code grabs data from one sheet and inserts/formats it into another sheet in two separate tables that have been converted into range. I did this because I need to automatically update two different graphs with certain data. I'm still new with VBA coding and any kind of direction or help to make the code run faster is appreciated, whether that be tips or ways to get rid of unnecessary code since it is probably longer than it needs to be.
Public Sub Button1_Click() ' Update Button
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Dim lastRowPart As Long
Dim lastRowCW As Long
Dim lastRowQty As Long
Dim lastRowQtyLeft As Long
Dim lastRowDescrip As Long
Dim i, j, k As Integer
Dim IO As Worksheet: Set IO = Sheets("Inventory Overview")
Dim TD As Worksheet: Set TD = Sheets("Trend Data")
'1. Copies and formats data
lastRowPart = IO.Cells(Rows.count, "F").End(xlUp).Row
lastRowDescrip = IO.Cells(Rows.count, "G").End(xlUp).Row
lastRowQtyLeft = IO.Cells(Rows.count, "O").End(xlUp).Row
lastRowQty = IO.Cells(Rows.count, "I").End(xlUp).Row
lastRowCW = IO.Cells(Rows.count, "L").End(xlUp).Row
TD.Cells.UnMerge ' reset***
j = 2
k = 2
For i = 2 To lastRowCW
If IO.Cells(i, "L").Value = "Unknown" Then
TD.Cells(j, "G").Value = IO.Cells(i, "L").Value
TD.Cells(j, "H").Value = IO.Cells(i, "F").Value
TD.Cells(j, "I").Value = IO.Cells(i, "I").Value
TD.Cells(j, "J").Value = IO.Cells(i, "O").Value
TD.Cells(j, "K").Value = IO.Cells(i, "G").Value
j = j + 1
Else
TD.Cells(k, "A").Value = IO.Cells(i, "L").Value
TD.Cells(k, "B").Value = IO.Cells(i, "F").Value
TD.Cells(k, "C").Value = IO.Cells(i, "I").Value
TD.Cells(k, "D").Value = IO.Cells(i, "O").Value
TD.Cells(k, "E").Value = IO.Cells(i, "G").Value
k = k + 1
End If
Next
' Autofit
TD.range("B1:B" & lastRowPart).Columns.AutoFit
TD.range("E1:E" & lastRowDescrip).Columns.AutoFit
TD.range("H1:H" & lastRowPart).Columns.AutoFit
TD.range("K1:K" & lastRowDescrip).Columns.AutoFit
'2. Sort Cells
Dim LastRow As Long
LastRow = TD.Cells(Rows.count, 5).End(xlUp).Row
With TD.Sort ' sorts data from A to Z
.SetRange TD.range("A2:E" & LastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'3. Merge CW Cells
' rngMerge = range for parts reworked/left with known CW
' URngMerge = range for parts reported with unknown CW
Dim rngMerge As range, URngMerge As range, cell As range, lastRowMerge As Long, ULastRowMerge As Long
lastRowMerge = TD.Cells(Rows.count, 1).End(xlUp).Row
ULastRowMerge = TD.Cells(Rows.count, 7).End(xlUp).Row
Set rngMerge = TD.range("A1:A" & lastRowMerge)
Set URngMerge = TD.range("G1:G" & ULastRowMerge)
MergeAgain:
For Each cell In rngMerge
If cell.Value = cell.Offset(1, 0).Value And IsEmpty(cell) = False Then
range(cell, cell.Offset(1, 0)).Merge
GoTo MergeAgain
End If
Next
MergeAgain2:
For Each cell In URngMerge
If cell.Value = cell.Offset(1, 0).Value And IsEmpty(cell) = False Then
range(cell, cell.Offset(1, 0)).Merge
GoTo MergeAgain2
End If
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
Related
Currently i have this code to register daily all meals at a canteen.
Sub register()
Dim s As Worksheet
Set s = Worksheets("Lista_" & Range("K9").Value)
Dim row As Long
row = s.Cells(s.Rows.Count, "B").End(xlUp).row + 1
s.Cells(row, "B").Value = Range("C7").Value
s.Cells(row, "C").Value = Range("C9").Value
s.Cells(row, "H").Value = Range("L9").Value
s.Cells(row, "I").Value = Range("P20").Value
s.Cells(row, "N").Value = Range("P21").Value
s.Cells(row, "O").Value = Range("P1").Value
Range("M6:M19").Select
Range("M19").Activate
Selection.ClearContents
Range("C7:D7").Select
Selection.ClearContents
Range("C7").Select
End Sub
--
I would like there to be a message if an employee's number has already been registered (so as not to duplicate it)... for that the vba code should search on all pages if that number already existed or not.If the number appears in column B of sheets that begin with "Lista_" a message should appear
I think you just need to add a function that checks for the employee number
Something like this worked for me using your sample data
You can change constants and data types to match your situation
Option Explicit
Sub register()
Dim s As Worksheet
Dim row As Long
Dim employeeNum As String
Set s = Worksheets("Lista_" & Range("K9").Value)
row = s.Cells(s.Rows.Count, "B").End(xlUp).row + 1
employeeNum = Range("C7").Value
If AlreadyRegistered(employeeNum) Then
MsgBox "Ignoring Preexisting Employee Number: " & employeeNum
Else
s.Cells(row, "B").Value = employeeNum
s.Cells(row, "C").Value = Range("C9").Value
s.Cells(row, "H").Value = Range("L9").Value
s.Cells(row, "I").Value = Range("P20").Value
s.Cells(row, "N").Value = Range("P21").Value
s.Cells(row, "O").Value = Range("P1").Value
Range("M6:M19").Select
Range("M19").Activate
Selection.ClearContents
Range("C7:D7").Select
Selection.ClearContents
Range("C7").Select
End If
End Sub
Function AlreadyRegistered(employeeNum As String) As Boolean
Const EmployeeColumn As String = "B:B"
Dim varSheets As Variant
Dim intSheet As Integer
Dim xlSheet As Worksheet
Dim rgeFound As Range
Dim i As Integer
AlreadyRegistered = False
varSheets = Array("Lista_AA", "Lista_BB", "Lista_CC")
For intSheet = LBound(varSheets) To UBound(varSheets)
Set xlSheet = Sheets(varSheets(i))
Set rgeFound = xlSheet.Range(EmployeeColumn).Find(employeeNum)
If Not (rgeFound Is Nothing) Then
AlreadyRegistered = True
Exit For
End If
Next intSheet
End Function
I have wrote a code which is working like Turtle walks. I have added Application Functions to make it faster but code has decided that he has to work slowly.
Any expert help will be appreciated.
Dim LastRowColumnA As Long
Dim i As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
LastRowColumnA = Sheet1.Cells(Rows.Count, 1).End(xlUp).Row
For i = 11 To LastRowColumnA
If Sheet1.Cells(i, 1).Value <> "" Then
Cells(i, 7) = Evaluate("=INDEX(Table1!$A$1:$DP$27,MATCH($G$9&$E$4,Table1!$E:$E&Table1!$F:$F,0),MATCH(A" & i & ",Table1!$6:$6,0))")
Cells(i, 8) = Evaluate("=INDEX(Table1!$A$1:$DP$27,MATCH($H$9&$E$4,Table1!$E:$E&Table1!$F:$F,0),MATCH(A" & i & ",Table1!$6:$6,0))")
Cells(i, 9) = Evaluate("=INDEX(Table1!$A$1:$DP$27,MATCH($I$9&$E$4,Table1!$E:$E&Table1!$F:$F,0),MATCH(A" & i & ",Table1!$6:$6,0))")
Cells(i, 10) = Evaluate("=INDEX(Table1!$A$1:$DP$27,MATCH($J$9&$E$4,Table1!$E:$E&Table1!$F:$F,0),MATCH(A" & i & ",Table1!$6:$6,0))")
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End If
Next
second approach.
Dim LastRowColumnA As Long
LastRowColumnA = Sheet1.Cells(Rows.Count, 4).End(xlUp).Row
Sheet1.Range("G10").FormulaArray = _
"=IFERROR(INDEX(Table1!R1C1:R27C120,MATCH(R9C7&R4C5,Table1!C5&Table1!C6,0),MATCH(RC[-6],Table1!R6,0)), """")"
Sheet1.Range("G10").AutoFill Destination:=Sheet1.Range("G10:G" & LastRowColumnA), Type:=xlFillDefault
Sheet1.Range("H10").FormulaArray = _
"=IFERROR(INDEX(Table1!R1C1:R27C120,MATCH(R9C8&R4C5,Table1!C5&Table1!C6,0),MATCH(RC[-7],Table1!R6,0)), """")"
Sheet1.Range("H10").AutoFill Destination:=Sheet1.Range("H10:H" & LastRowColumnA), Type:=xlFillDefault
Sheet1.Range("I10").FormulaArray = _
"=IFERROR(INDEX(Table1!R1C1:R27C120,MATCH(R9C9&R4C5,Table1!C5&Table1!C6,0),MATCH(RC[-8],Table1!R6,0)), """")"
Sheet1.Range("I10").AutoFill Destination:=Sheet1.Range("I10:I" & LastRowColumnA), Type:=xlFillDefault
Sheet1.Range("J10").FormulaArray = _
"=IFERROR(INDEX(Table1!R1C1:R27C120,MATCH(R9C9&R4C5,Table1!C5&Table1!C6,0),MATCH(RC[-9],Table1!R6,0)), """")"
Sheet1.Range("J10").AutoFill Destination:=Sheet1.Range("J10:J" & LastRowColumnA), Type:=xlFillDefault
Formulas of First Cells which has been converted to code.
=IFERROR(INDEX(Table1!$A$1:$DP$27,MATCH($G$9&$E$4,Table1!$E:$E&Table1!$F:$F,0),MATCH(A10,Table1!$6:$6,0)), "")
=IFERROR(INDEX(Table1!$A$1:$DP$27,MATCH($H$9&$E$4,Table1!$E:$E&Table1!$F:$F,0),MATCH(A10,Table1!$6:$6,0)), "")
=IFERROR(INDEX(Table1!$A$1:$DP$27,MATCH($I$9&$E$4,Table1!$E:$E&Table1!$F:$F,0),MATCH(A10,Table1!$6:$6,0)), "")
=IFERROR(INDEX(Table1!$A$1:$DP$27,MATCH($J$9&$E$4,Table1!$E:$E&Table1!$F:$F,0),MATCH(A10,Table1!$6:$6,0)), "")
as per my comment:
Find the rows outside the loop as they will all be the same, then just find the column in the loop. It will cut down on the number of calc.
Dim LastRowColumnA As Long
Dim i As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With Sheet1
LastRowColumnA = Sheet1.Cells(Rows.Count, 1).End(xlUp).Row
Dim gRow As Variant
gRow = .Evaluate("MATCH($G$9&$E$4,Table1!$E1:$E27&Table1!$F1:$F27,0)")
Dim hRow As Variant
hRow = .Evaluate("MATCH($H$9&$E$4,Table1!$E1:$E27&Table1!$F1:$F27,0)")
Dim iRow As Variant
iRow = .Evaluate("MATCH($I$9&$E$4,Table1!$E1:$E27&Table1!$F1:$F27,0)")
Dim jRow As Variant
jRow = .Evaluate("MATCH($J$9&$E$4,Table1!$E1:$E27&Table1!$F1:$F27,0)")
For i = 11 To LastRowColumnA
If Sheet1.Cells(i, 1).Value <> "" And Not IsError(gRow) And Not IsError(hRow) And Not IsError(iRow) And Not IsError(jRow) Then
Dim clm As Variant
clm = Application.Match(.Range("A" & i), Worksheets("Table1").Range("6:6"), 0)
If Not IsError(clm) Then
.Cells(i, 7) = Worksheets("Table1").Cells(gRow, clm)
.Cells(i, 8) = Worksheets("Table1").Cells(hRow, clm)
.Cells(i, 9) = Worksheets("Table1").Cells(iRow, clm)
.Cells(i, 10) = Worksheets("Table1").Cells(jRow, clm)
End If
End If
Next
End With
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
If that is still too slow then one will need to use variant arrays and skip looping the ranges as this is slow.
I have an inventory sheet.
When a researcher takes a quantity in a specific lot, the quantity is removed first from the stock quantity, the specific row of ComboBox1 in the column #8. It's okay for that part.
When a second quantity is taken and the row of the lot is not empty, I put the data in a row under but I want the quantity (textBox2) to be substract to the column #8 of the row of ComboBox1.
Private Sub CommandButton1_Enter()
Dim emptyRow As Long
Dim ws As Worksheet
Set ws = ActiveSheet
ActiveSheet.Name = "Micrux"
Dim iLastRow As Long, iFound As Long
Dim rng, bEmpty As Boolean, c As Integer
Dim Test As Boolean
bEmpty = True
With ws
iLastRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rng = .Range("A1:A" & iLastRow + 1).Find(ComboBox1.Value, _
After:=.Range("A" & iLastRow + 1), _
LookIn:=xlValues, _
lookat:=xlWhole, _
searchorder:=xlByRows, _
SearchDirection:=xlPrevious)
Test = (TextBox2.Text) > ws.Cells(iLastRow, 8)
If Test = True Then
MsgBox "Not enough quantity in stock!"
Else
If rng Is Nothing Then
iFound = iLastRow + 1
Else
iFound = rng.Row
For c = 4 To 7
If Len(.Cells(iFound, c)) > 0 Then bEmpty = False
Next
If bEmpty = False Then
iFound = iFound + 1
.Cells(iFound, 1).EntireRow.Insert xlShiftDown
.Cells(iFound, 7).Value = TextBox2.Text
.Cells(iFound, 6).Value = TextBox3.Text
.Cells(iFound, 5).Value = ComboBox2.Value
.Cells(iFound, 4).Value = TextBox1.Text
Else
.Cells(iFound, 7).Value = TextBox2.Text
.Cells(iFound, 6).Value = TextBox3.Text
.Cells(iFound, 5).Value = ComboBox2.Value
.Cells(iFound, 4).Value = TextBox1.Text
End If
End If
End If
End With
Unload Me
End Sub
I'm trying to copy a range from one sheet to another, but ignoring blank rows, and making sure there aren't blank rows in the destination.
After looking on this site, I've successfully used the code below.
However, I want to expand this to a large data range and it seems to take an absolute age. Any ideas on a more efficient code? Slight newbie here!
Thanks!
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Dim Source As Worksheet
Dim Destination As Worksheet
Dim i As Integer
Dim j As Integer
Set Source = Sheet1
Set Destination = Sheet4
j = 2
For i = 9 To 10000
If Source.Cells(i, 2).Value <> "" Then
Destination.Cells(j, 1).Value = Source.Cells(i, 1).Value
Destination.Cells(j, 2).Value = Source.Cells(i, 2).Value
Destination.Cells(j, 3).Value = Source.Cells(i, 3).Value
Destination.Cells(j, 4).Value = Source.Cells(i, 4).Value
Destination.Cells(j, 5).Value = Source.Cells(i, 5).Value
Destination.Cells(j, 6).Value = Source.Cells(i, 6).Value
Destination.Cells(j, 7).Value = Source.Cells(i, 7).Value
Destination.Cells(j, 8).Value = Source.Cells(i, 8).Value
Destination.Cells(j, 9).Value = Source.Cells(i, 9).Value
j = j + 1
End If
Next i
Application.ScreenUpdating = True
Application.EnableEvents = True
ActiveSheet.DisplayPageBreaks = True
Application.Calculation = xlCalculationAutomatic
End Sub
[Edited to add a bit of clarity]
Replace your for loop with codes below.
Method 1: union all the range you would like to copy, and paste them at once.
Dim copyRange As Range
For i = 9 To 10000
If Source.Cells(i, 2).Value <> "" Then
If copyRange Is Nothing Then
Set copyRange = Source.Range(Source.Cells(i, 1), Source.Cells(i, 9))
Else
Set copyRange = Union(copyRange, Source.Range(Source.Cells(i, 1), Source.Cells(i, 9)))
End If
End If
Next i
copyRange.Copy Destination.Cells(2, 1)
Method 2(recommended): Use an autofilter for filtering the data.
Dim sourceRng As Range
Set sourceRng = Source.Range(Source.Cells(9, 1), Source.Cells(10000, 9))
sourceRng.AutoFilter Field:=2, Criteria1:="<>"
sourceRng.Copy Destination.Cells(2, 1)
Source.AutoFilterMode = False
Looping through worksheet rows is almost the slowest way to process data blocks. The only thing slower is looping through both rows and columns.
I'm not sure how many records you have but this processed 1500 rows of dummy data in ~0.14 seconds.
Option Explicit
Sub Macro4()
Dim wsSource As Worksheet, wsDestination As Worksheet
Dim i As Long, j As Long, k As Long, arr As Variant
On Error GoTo safe_exit
appTGGL bTGGL:=False
Set wsSource = Sheet1
Set wsDestination = Sheet4
'collect values from Sheet1 into array
With wsSource
arr = .Range(.Cells(9, "A"), .Cells(.Rows.Count, "B").End(xlUp).Offset(0, 7)).Value
End With
'find first blank in column B
For j = LBound(arr, 1) To UBound(arr, 1)
If arr(j, 2) = vbNullString Then Exit For
Next j
'collect A:I where B not blank
For i = j To UBound(arr, 1)
If arr(i, 2) <> vbNullString Then
For k = 1 To 9: arr(j, k) = arr(i, k): Next k
j = j + 1
End If
Next i
'clear remaining rows
For i = j To UBound(arr, 1)
For k = 1 To 9: arr(i, k) = vbNullString: Next k
Next i
'put values sans blanks into Sheet4
With wsDestination
.Cells(2, "A").Resize(UBound(arr, 1), UBound(arr, 2)) = arr
End With
safe_exit:
appTGGL
End Sub
Sub appTGGL(Optional bTGGL As Boolean = True)
With Application
.EnableEvents = bTGGL
.ScreenUpdating = bTGGL
.DisplayAlerts = bTGGL
.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
End With
Debug.Print IIf(bTGGL, "end: ", "start: ") & Timer
End Sub
I have a macro that runs through the data and outputs it in a specific format to a different sheet. Issue is, it errors out after row 65,536 has been populated, which I'm guessing is a limit imposed by MS:
Sub Macro1()
'
' Macro1 Macro
'
'
Sheets.Add.Name = "Sheet2"
Worksheets("Sheet2").Cells(1, 1).Value = "datacol1"
Worksheets("Sheet2").Cells(1, 2).Value = "datacol2"
Worksheets("Sheet2").Cells(1, 3).Value = "datacol3"
Worksheets("data").Activate
SourceColumn = 2
SourceRow = 2
Cells(SourceRow, 1).Activate
targetRow = SourceRow
targetcolumn = 1
While Cells(1, SourceColumn).Value <> ""
While ActiveCell.Value <> ""
Worksheets("Sheet2").Cells(targetRow, targetcolumn).Value = ActiveCell.Value
Worksheets("Sheet2").Cells(targetRow, targetcolumn + 1).Value = Worksheets("Data").Cells(1, SourceColumn).Value
Worksheets("Sheet2").Cells(targetRow, targetcolumn + 2).Value = Worksheets("Data").Cells(SourceRow, SourceColumn).Value
SourceRow = SourceRow + 1
targetcolumn = 1
targetRow = targetRow + 1
Cells(SourceRow, 1).Activate
Wend
SourceColumn = SourceColumn + 1
SourceRow = 2
Cells(SourceRow, 1).Activate
Wend
With Worksheets("Sheet2").Sort
.SetRange Range(Cells(2, 1), Cells(targetRow, 3))
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
What can I add in this macro so when it does hit this limit of 65,536 it creates a new worksheet and keeps going?
I don't think it's an Excel limitation. I think the issue is your variable is undefined, so VBA is taking its best guess using a datatype that is insufficient to handle the row number you are giving it. By changing the datatype to a long, I think you will fix your issue.
As a demonstration, this should address both issues. For illustration purposes, I made the sheets split at 100,000 rows (rather than 65k to show it will work), but you can change this to 1,000,000 or whatever the real limit is. I made it 100,000 just to prove it does split.
Sub Macro1()
Dim wsFrom, wsTo As Worksheet
Dim SourceRow, SheetNumber, TargetRow As Long
Dim val As String
Set wsFrom = Worksheets("data")
SheetNumber = 2
SourceRow = 2
val = wsFrom.Cells(SourceRow, 1).Text
While val <> ""
Sheets.Add.Name = "Sheet" & SheetNumber
Set wsTo = Worksheets("Sheet" & SheetNumber)
wsTo.Cells(1, 1).Value = "datacol1"
wsTo.Cells(1, 2).Value = "datacol2"
wsTo.Cells(1, 3).Value = "datacol3"
TargetRow = 2
While val <> "" And TargetRow < 100000
wsTo.Cells(TargetRow, 1).Value = wsFrom.Cells(SourceRow, 1).Value
wsTo.Cells(TargetRow, 2).Value = wsFrom.Cells(SourceRow, 2).Value
wsTo.Cells(TargetRow, 3).Value = wsFrom.Cells(SourceRow, 3).Value
SourceRow = SourceRow + 1
TargetRow = TargetRow + 1
val = wsFrom.Cells(SourceRow, 1).Text
Wend
' insert your sort logic here
SheetNumber = SheetNumber + 1
Wend
End Sub