I'm writing a VBA program to search through a large spreadsheet and copy rows that have the same account five or more times associated with the data to a different sheet. The program does exactly what it's supposed to do when I step through each individual line (F8), but when I run the program (F5), it doesn't end up copying any information to the second sheet. I've tried adding a two second delay between switching sheets and pasting the data, just in case this was the problem, but so far it hasn't helped.
Any suggestions?
Edit: I thought that the screen updating might be causing the problem, so I disabled it. The program still didn't paste the data in the other worksheet.
Second Edit: I noticed that when I put a stop in at the beginning of the while loop and step the program through in chunks, it also does not copy and paste the data like it should be. It still works when stepping through individual lines of code, though. I also removed the 2 second pauses as those didn't make a difference.
Here's the code:
Public Sub Main()
Worksheets(2).Activate
Range("A1").Select
Worksheets(1).Activate
Range("C2").Select
AcctName = ActiveCell.Value
LoopControl = 0
AcctNameCt = 1
CurrentAcctRow = ActiveCell.Row
Do While LoopControl <> 1
SecondLoopControl = 0
If AcctName = ActiveCell.Offset(AcctNameCt, 0).Value Then
AcctNameCt = AcctNameCt + 1
If AcctNameCt > 4 Then
GreaterThanFour
End If
ElseIf ActiveCell.Offset(AcctNameCt, 0).Value = "" Then
Exit Do
Else
ActiveCell.Offset(AcctNameCt, 0).Activate
AcctName = ActiveCell.Value
AcctNameCt = 1
CurrentAcctRow = ActiveCell.Row
End If
Loop
End Sub
Public Sub CopyData()
Dim EndRow As Integer
Dim StopCopy As Integer
Dim RestartRow As Integer
EndRow = CurrentAcctRow + AcctNameCt
StopCopy = EndRow - 1
RestartRow = EndRow + 1
ActiveSheet.Range("C" & CurrentAcctRow & ":" & "C" & StopCopy).EntireRow.Copy
Worksheets(2).Activate
LookForEmptyRow
ActiveCell.EntireRow.PasteSpecial
CurrentAcctRow = CurentAcctRow + 1
Worksheets(1).Activate
Range("C" & EndRow).Select
AcctNameCt = 0
End Sub
Public Sub GreaterThanFour()
Do While SecondLoopControl <> 1
If AcctName = ActiveCell.Offset(AcctNameCt, 0).Value Then
AcctNameCt = AcctNameCt + 1
Else
CopyData
SecondLoopControl = 1
End If
Loop
End Sub
Public Sub LookForEmptyRow()
Range("A1").Select
Dim LookAnotherLoopControl As Integer
LookAnotherLoopControl = 0
Do While LookAnotherLoopControl <> 1
If ActiveCell.Value = "" Then Exit Sub Else ActiveCell.Offset(1, 0).Activate
Loop
End Sub
I set the worksheet names to variables and called those, rather than calling the worksheets directly. For some reason, this works better.
Set wbA = Workbooks(Workbook Name)
Set wsA = Worksheets(Worksheet Name 1)
Set wsB = Worksheets(Worksheet Name 2)
Where the "Workbook Name" and "Worksheet Name 1" reflect the actual names, instead. Those are working better than:
Worksheets(2).Activate
LookForEmptyRow
ActiveCell.EntireRow.PasteSpecial
CurrentAcctRow = CurentAcctRow + 1
Worksheets(1).Activate
Range("C" & EndRow).Select
I also used a better method to look for an empty row, rather than writing my own subroutine. The original code had this sub that I wrote:
Public Sub LookForEmptyRow()
Range("A1").Select
Dim LookAnotherLoopControl As Integer
LookAnotherLoopControl = 0
Do While LookAnotherLoopControl <> 1
If ActiveCell.Value = "" Then Exit Sub Else ActiveCell.Offset(1, 0).Activate
Loop
Which, while effective, was highly inefficient. I replaced it with the much more efficient line of code:
lRow = Range("A1000").End(xlUp).Row
Cells(lRow + 1, 1).Activate
I'm doing work to automate a ledger of mine based on the FIFO accounting principle (First in First out), where anything that would be referred to as a contra balance would be subtracted from the first entry, then the second, until that variable is zero (or if there is residual begin a new accounting line).
Generally what I have been doing to add a new position to this ledger (not removing any balance simply creating a line item is this...
Tickerstring = TTB 'TTB is the user defined input for the ticker
tickercolumn = HBWS.Cells.Find(What:="Ticker").Column 'Use this to identify
what column the ticker field is
Set TickerResult = HBWS.Cells.Find(What:=TickerString, LookIn:=xlValues)
If Not TickerResult Is Nothing Then
tickerRow = TickerResult.Row
Else
End If 'Identifies the row which the actual Ticker is in i.e. the TTB
HBWS.Cells(tickerRow, tickercolumn) = TTB
I use that same concept to define the amount of Shares, and whether they are long and short. Inserting Userform inputs into the respective cells.
My question is, say I run that code 3 times and now have 3 lines items that look like this
AAPL 300 Long
AAPL 100 Long
AAPL 100 Long
Then I want to add a new position for 600 short, which would go through the FIFO accounting process and remove 300 from the first row, 100 from the second, 100 from the third, then create a new line with the 100 short. How would I go about doing that?
I would imagine that I would be subtracting from a user defined variable i.e take 300 out of the first row, now my defined variable is left at 300 (when it started at 600).
Basically i think the best way to describe this would be how do I subtract from a variable based on current workbook values, then continue using this in my sub procedure.
EDIT: Editing my post for clarity
i have the following entry in my spreadsheet
I want to run my macro to take my short position indicated in the below userform subtract it from currently in my spread sheet then create a residual line representing what is left in the short position
The end state should look like this
Let me know if you need additional info
Requirements:
Maintain a ledger of shares transactions, generated from an user form input (one at a time).
Calculate & show the net position of the shares, using the FIFO inventory valuation method.
Proposed Solution:
The requirements can be achieved using:
A ListObject to contain the ledger of transactions and to calculate the end position after each transaction.
A PivotTable to show the end position of the shares (and any other report needed).
The figure below shows the proposed ListObject and PivotTable
ListObject Fields:
Input from user form
Ticker : Share symbol.
L/S : Share position (Long\Short).
Lots : Quantity of shares.
Calculated by VBA procedure
L/S.Net: Net share position (Long\Short).
Qty: Net share quantity (absolute value).
Lots.Net: Net share quantity.
T: Record Type (P: Prior \ R: Residual), used to flag the latest transaction of a share.
TimeStamp: Record date & time of posting, used to apply the FIFO valuation method.
VBA Procedure:
See explanations\coments inserted in the procedure.
Option Private Module
Option Compare Text
Option Explicit
Option Base 1
Rem Updated 20180504_121918
Sub ListObject_Stocks_Ledger_FIFO(vRcrd As Variant)
Dim aFlds As Variant, vFld As Variant
aFlds = [{"Ticker","L/S","Lots","T","TimeStamp","Lots.Net","L/S.Net","Qty"}]
Dim lo As ListObject, pt As PivotTable
Dim sTicker As String, lCnt As Long, lPos As Long
Dim lRow As Long, bCol As Byte, b As Byte
Dim sFml As String
Dim vValue As Variant
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Rem Set Objects
With ThisWorkbook.Worksheets("Sht(0)") 'change as required
Application.Goto .Cells(1), 1
Set pt = .PivotTables("ptPositions")
Set lo = .ListObjects("loPositions")
End With
With lo
Rem Set ListObject New Row
lRow = 1 + .ListRows.Count
Select Case lRow
Case 1
Rem ListObject with zero records
.HeaderRowRange.Cells(2, 1).Value2 = "!NEW"
Case Else
vFld = "Ticker"
sTicker = vRcrd(1)
bCol = .ListColumns(vFld).Index
lCnt = WorksheetFunction.CountIfs(.DataBodyRange.Columns(bCol), sTicker)
Rem Flag prior Ticker records
Select Case lCnt
Case 0
Rem New Ticker - NO ACTION
Case 1
Rem Ticker with only one prior record
lPos = WorksheetFunction.Match(sTicker, .DataBodyRange.Columns(bCol), 0)
.ListColumns("T").DataBodyRange.Cells(lPos).Value2 = "P"
Case Else
Rem Ticker with only one prior record
.Range.AutoFilter Field:=bCol, Criteria1:=sTicker
.ListColumns("T").DataBodyRange.SpecialCells(xlCellTypeVisible).Value2 = "P"
.Range.AutoFilter
End Select: End Select
Rem Add New Record
For Each vFld In aFlds
b = 1 + b
bCol = .ListColumns(vFld).Index
Rem Set Field Value\Formula
sFml = vbNullString
vValue = vbNullString
Select Case vFld
Case "Ticker", "L/S", "Lots": vValue = vRcrd(b)
Case "T": vValue = "R"
Case "TimeStamp": vValue = CDbl(Now)
Case "L/S.Net"
sFml = "=IF(NOT(EXACT([#T],'R')),CHAR(39)," & vbLf _
& "IF([#[Lots.Net]]<0,'Short',IF([#[Lots.Net]]>0,'Long','Zero')))"
Case "Qty"
sFml = "=IF(NOT(EXACT([#T],'R')),CHAR(39)," & vbLf _
& "ABS([#[Lots.Net]]))"
Case "Lots.Net"
sFml = "=IF(NOT(EXACT([#T],'R')),CHAR(39),SUM(" & vbLf _
& "SUMIFS([Lots],[Ticker],[#Ticker],[L/S],'Long',[TimeStamp],'<='&[#TimeStamp])," & vbLf _
& "-SUMIFS([Lots],[Ticker],[#Ticker],[L/S],'Short',[TimeStamp],'<='&[#TimeStamp])))"
End Select
Rem Apply Field Value\Formula
Select Case vbNullString
Case Is <> vValue
.DataBodyRange.Cells(lRow, bCol).Value2 = vValue
Case Is <> sFml
sFml = Replace(sFml, Chr(39), Chr(34))
With .DataBodyRange.Columns(bCol)
.Formula = sFml
.Value2 = .Value2
End With: End Select: Next: End With
Rem Sort ListObject
With lo.Sort
With .SortFields
.Clear
.Add Key:=lo.ListColumns("Ticker").DataBodyRange, _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Add Key:=lo.ListColumns("TimeStamp").DataBodyRange, _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
End With
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Rem Refresh PivotTable
pt.PivotCache.Refresh
Application.EnableEvents = False
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = False
End Sub
Use this procedure to simulate the posting from the user form:
Sub ListObject_Stocks_Ledger_FIFO_TEST()
Dim aDATA As Variant, vRcrd As Variant
aDATA = Array( _
Array("AAPL", "Long", "300"), _
Array("AAPL", "Long", "100"), _
Array("AAPL", "Long", "100"), _
Array("AAPL", "Short", "600"), _
Array("BCS", "Long", "300"), _
Array("BCS", "Long", "100"), _
Array("BCS", "Short", "500"), _
Array("Test", "Long", "100"), _
Array("Test", "Long", "200"), _
Array("Test", "Long", "300"), _
Array("Test", "Short", "400"))
For Each vRcrd In aDATA
Call ListObject_Stocks_Ledger_FIFO(vRcrd)
: Stop
Next
End Sub
This could be an example of what you want to do, hopefully is usefull to you:
Sub test()
Dim reduce_amount As String
reduce_amount = Val(InputBox("Number:"))
Dim cell As Range
For Each cell In Selection
cell_value = Mid(cell.Value, 6, 3)
If IsNumeric(cell_value) Then
reduce_amount = reduce_amount - cell_value
End If
Next cell
If reduce_amount > 0 Then
Selection.End(xlDown).Offset(1, 0).Value = "AAPL " & reduce_amount & " Long"
End If
End Sub
I think you should make it so each transaction stands on it's own (unless you have a good reason to do it otherwise). I never store "state" in a cell if possible. What about keeping track of each bucket. Here's an example
Public Sub AddLots(ByVal Ticker As String, ByVal Lot As Double)
Dim rCell As Range
Dim LotRemains As Double
Dim dc As Scripting.Dictionary
Dim dToTake As Double
Dim ThisTicker As String, ThisLS As String, ThisLot As Double, ThisBucket As Long, ThisTotal As Double
Dim lo As ListObject
Dim aOutput() As Variant
Dim MaxBucket As Long
Dim i As Long
LotRemains = Lot
Set dc = New Scripting.Dictionary
Set lo = Sheet1.ListObjects(1)
For Each rCell In lo.ListColumns(1).DataBodyRange.Cells
'Store this row's values
ThisTicker = rCell.Value: ThisLS = rCell.Offset(0, 1).Value: ThisLot = rCell.Offset(0, 2).Value
ThisBucket = rCell.Offset(0, 3).Value: ThisTotal = rCell.Offset(0, 4).Value
'if the ticker is the same
If ThisTicker = Ticker Then
'if it's going the opposite way of our transaction
If (Lot > 0 And ThisLS = "Short") Or _
(Lot < 0 And ThisLS = "Long") Then
'if there's still something left in the bucket
If ThisTotal <> 0 Then
If Abs(ThisTotal) >= Abs(LotRemains) Then
dToTake = LotRemains
Else
dToTake = -ThisTotal
End If
'store this bucket
dc.Add ThisTicker & "|" & ThisBucket, dToTake
'reduce the amount left to test
LotRemains = LotRemains - dToTake
'stop looking if we've used it all up
If LotRemains = 0 Then Exit For
End If
End If
End If
Next rCell
'this is an array we'll write out to the worksheet
ReDim aOutput(1 To dc.Count + IIf(LotRemains <> 0, 1, 0), 1 To 4)
'for every bucket we saved, put it in the array
For i = 1 To dc.Count
aOutput(i, 1) = Ticker
aOutput(i, 2) = IIf(Lot > 0, "Long", "Short")
aOutput(i, 3) = Abs(dc.Items(i - 1))
aOutput(i, 4) = Split(dc.Keys(i - 1), "|")(1)
Next i
'if we couldn't use it all up, get the next bucket number
If LotRemains <> 0 Then
For Each rCell In lo.ListColumns(1).DataBodyRange.Cells
If rCell.Value = Ticker Then
If rCell.Offset(0, 3).Value > MaxBucket Then
MaxBucket = rCell.Offset(0, 3).Value
End If
End If
Next rCell
'then add a new bucket to the array
aOutput(dc.Count + 1, 1) = Ticker
aOutput(dc.Count + 1, 2) = IIf(Lot > 0, "Long", "Short")
aOutput(dc.Count + 1, 3) = Abs(LotRemains)
aOutput(dc.Count + 1, 4) = MaxBucket + 1
End If
'write out the new transactions to the worksheet
lo.ListRows.Add.Range.Resize(UBound(aOutput, 1), UBound(aOutput, 2)).Value = aOutput
End Sub
Start here
Then run AddLots "BCS", 400 and get
Then run AddLots "BCS", -1000 and get
Then use a pivot table to see where you are by ticker, by bucket, or whatever
The formula in the last column of the table is
=SUMPRODUCT(([Ticker]=[#Ticker])*([Bucket]=[#Bucket])*([LS]="Long")*([Lots]))-SUMPRODUCT(([Ticker]=[#Ticker])*([Bucket]=[#Bucket])*([LS]="Short")*([Lots]))
I looked at all of the answers posted and took a few ideas from each. I used the below code to control the variable and then I have other code that kind of compiles everything using the final variable.
The section of code that clears out the other lots though and defines the variable is below
Dim rCell As Range
Dim reduce_amount As Variant
Dim HBWS As Worksheet
Dim TickerTotalString As String
TickerTotalString = "Total " & TTB
Set HBWS = Sheets("Hedgebook")
Dim FormulaWS As Worksheet
Set FormulaWS = Sheets("Formula_Template")
LastHBR = HBWS.Cells(HBWS.Rows.Count, "B").End(xlUp).Row
ClastHBC = HBWS.Cells(3, HBWS.Columns.Count).End(xlToLeft).Column
LastFWSR = FormulaWS.Cells(FormulaWS.Rows.Count, "B").End(xlUp).Row
CLASTFWSC = FormulaWS.Cells(3, FormulaWS.Columns.Count).End(xlToLeft).Column
Tickercolumn = HBWS.Cells.Find(What:="Ticker").Column
Datecolumn = HBWS.Cells.Find(What:="Date&Time Booked").Column
LScolumn = HBWS.Cells.Find(What:="L/S").Column
Lotscolumn = HBWS.Cells.Find(What:="Lots").Column
Conversioncolumn = HBWS.Cells.Find(What:="Conversion Cents").Column
Borrowcolumn = HBWS.Cells.Find(What:="Borrow (bps)").Column
Set Tickerresult = HBWS.Cells.Find(What:=TickerTotalString, LookIn:=xlValues)
If Not Tickerresult Is Nothing Then
Tickerrow = Tickerresult.Row
Else
End If
reduce_amount = LTB 'Userform input that defines the total lots
If reduce_amount > 0 Then
For Each rCell In HBWS.Range(Cells(3, Tickercolumn), Cells(LastHBR, Tickercolumn))
If rCell.Value = TTB And rCell.Offset(0, -1).Value <> TickerTotalString And reduce_amount > 0 Then
Cell_value = rCell.Offset(0, 3).Value
If reduce_amount < Cell_value Then
rCell.Offset(0, 3).Value = Cell_value - reduce_amount
ElseIf reduce_amount > Cell_value Then
rCell.Offset(0, 3).Value = 0
reduce_amount = reduce_amount - Cell_value
ElseIf reduce_amount = Cell_value Then
reduce_amount = 0
rCell.Offset(0, 3).Value = 0
End If
End If
Next
End If
I have a bunch of subroutines for a worksheet that all start and end exactly the same way. They all basically loop through every row in the table and perform some unique operation to each individual row. I do not want them all to run every time, instead I want to be able to call them individually.
Here's what the actual code looks like:
Sub randomSub()
Dim finalRow As Long
Dim i As Long
finalRow = Sheet.Cells(Rows.count, Column).End(xlUp).row
With Sheet2
For i = 3 To finalRow
' Do some random operations here
Next i
End With
End Sub
Setting the code up to loop through every row is not hard but after repeating it across 4 or 5 different subs I imagine there is a better way.
So I guess my question is this: Is there a best practice for avoiding writing a loop set-up like this over and over for every new sub I make?
EDIT: Here's some examples of the type of operations I'm doing, and would normally replace the ' Do random operations here
Sub moveToFront()
Dim stringHolder() As String
stringHolder = Split(Sheet2.Cells(i, 11), "; ")
If stringHolder(1) = "" Then
.Cells(i, 11) = stringHolder(0)
Else
.Cells(i, 11) = stringHolder(1) & "; " & stringHolder(0)
End If
End Sub
And another
Sub fillInTotals()
If .Cells(i, 3) <> "" Then
.Cells(i, 1) = "='EUS Graph'!$C$" & _
Application.WorksheetFunction.Match(.Cells(i, 3), Sheet4.Range("$A:$A"), 0)
Else
.Cells(i, 1) = "='EUS Graph'!$C$"
End If
End Sub
Consider using a generalized user-defined function and place it where all macros have access to it, either the ThisWorkbook section or a standard module:
Public Function randomSub(SheetName As String, ColumnLetter As String, _
OperationType As String)
Dim wsh As Worksheet
Dim i As Long, finalRow As Long
Dim stringHolder() As String
Set wsh = ThisWorkbook.Worksheets(SheetName)
With wsh
finalRow = wsh.Cells(wsh.Rows.Count, ColumnLetter).End(xlUp).Row
For i = 3 To finalRow
Select Case OperationType
Case "MoveToFront"
stringHolder = Split(Sheet2.Cells(i, 11), "; ")
If stringHolder(1) = "" Then
.Cells(i, 11) = stringHolder(0)
Else
.Cells(i, 11) = stringHolder(1) & "; " & stringHolder(0)
End If
Case "fillInTotals"
If .Cells(i, 3) <> "" Then
.Cells(i, 1) = "='EUS Graph'!$C$" & _
Application.WorksheetFunction.Match(.Cells(i, 3), _
Sheet4.Range("$A:$A"), 0)
Else
.Cells(i, 1) = "='EUS Graph'!$C$"
End If
End Select
Next i
End With
End Function
Then, call the function as needed, passing required parameters:
Call randomSub(ActiveSheet.Name, "A", "MoveToFront")
First, thanks for reading and any help offered.
I'm basically clueless here. I've spent the last several days trying to figure out how to code what I'd like done, and I'll try to explain it clearly.
My workbook has multiple sheets, but only two of them are of interest regarding this: Schedule & Shift.
On Schedule, there are 17 columns and 40-100 rows containing the employees name (column A) in one column, their initials (B), their employee number (C), their shift (D) and shift hours (E - which is returned via vlookup to another sheet).
Basically, I want a button that will copy the data from each of those 5 columns to the Shift sheet starting at "A3" and continue to copy down the rows in Schedule until it reaches a blank field for their name (which is column A).
So far, I've managed to copy the first row and the second row with the following code:
Private Sub CommandButton1_Click()
Dim i As Integer, IntName As String, IntInit As String, IntID As Integer, Shift As String, Hours As Integer
Worksheets("Schedule").Select
i = 1
IntName = Range("a4")
IntInit = Range("b4")
IntID = Range("C4")
Shift = Range("D4")
Hours = Range("E4")
Do While i < 5
Worksheets("Shift").Select
Worksheets("Shift").Range("a2").Select
If Worksheets("Shift").Range("a2").Offset(1, 0) <> "" Then
Worksheets("Shift").Range("a2").End(xlDown).Select
End If
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = IntName
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = IntInit
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = IntID
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = Shift
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = Hours
Worksheets("Schedule").Select
IntName = Worksheets("Schedule").Range("a4").Offset(1, 0)
IntInit = Worksheets("Schedule").Range("b4").Offset(1, 0)
IntID = Worksheets("Schedule").Range("c4").Offset(1, 0)
Shift = Worksheets("Schedule").Range("d4").Offset(1, 0)
Hours = Worksheets("Schedule").Range("e4").Offset(1, 0)
i = i + 1
Loop
End Sub
Obviously, this is clunky, and it doesn't actually do what I want beyond the 2nd time through the loop.
Any suggestions or pointers to help me move in the right direction?
Thanks again.
You're on the right path, you just need to nest our loop in another loop. Also, heed #BruceWayne's advice.
Private Sub CommandButton1_Click()
Dim i As Integer
Dim intCounter As Integer
Dim IntName As String
Dim IntInit As String
Dim IntID As Integer
Dim Shift As String
Dim Hours As Integer
'Adjust intCounter if you want to start on a row other than 1
intCounter = 1
Do
With Worksheets("Schedule")
IntName = .Cells(intCounter, 1).Value
IntInit = .Cells(intCounter, 2).Value
IntID = .Cells(intCounter, 3).Value
Shift = .Cells(intCounter, 4).Value
Hours = .Cells(intCounter, 5).Value
End With
If IntName = "" Then Exit Do
i = 1
Do While i < 5
'No need to use offset when you can just reference the cell directly.
'Also, not sure why you select this column anyhow.
'These lines can probably be deleted?
'If Worksheets("Shift").Range("a3").Value <> "" Then
' Worksheets("Shift").Range("a2").End(xlDown).Select
'End If
'Avoid using things like Select, ActiveCell, and ActiveSheet.
'What if someone clicks on something while your code is running?? Oops!
With Worksheets("Shift")
.Cells(i + 1, 2).Value = IntName
.Cells(i + 1, 3).Value = IntInit
.Cells(i + 1, 4).Value = IntID
.Cells(i + 1, 5).Value = Shift
.Cells(i + 1, 6).Value = Hours
End With
i = i + 1
Loop
'Increment to go to the next row of Schedule
intCounter = intCounter + 1
Loop
End Sub
brought in by Tim's concern about compact code, try this
Private Sub CommandButton1_Click()
With Worksheets("Schedule").Range("A4:E4").CurrentRegion
.Offset(1).Resize(.Rows.Count - 1).Copy Destination:=Worksheets("Shift").Range("A3")
End With
End Sub
The following macro is created to match employee names to badge numbers. It needs to be in excel and not access. There are two sheets in the workbook. "All" tracks the first name, second name, and other information. This workbooks is about 8000 rows at present and growing. "EmpCon List" (Employer / Contractor) is a database of their first name, second name and badge number and has a stable amount of rows about 450. There is a data validation between All and Emp Con so their names must match perfectly
The macro is designed to match the first and second name in "All" against the first name in "EmpCon List", and then match it to a badge number which is to appear in "All".
The macro appears to be logical, a double For loop. However, the program does not respond correctly and "whites out" after a few seconds of running. Is there a way to help VBA process this?
Sub BadgeNumberLookUp()
Dim i As Integer, j As Integer
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Sheets("All").Select ' Job Number page
JobRows = Application.CountA(Range("A:A")) + 10 ' This number is 8000 and growing
Sheets("EmpCon List").Select 'Employee / Contractors sheet
EmployeeCount = Application.CountA(Range("M:M")) + 10 ' This number is about 450 and stable
For i = 1 To JobRows
Sheets("All").Select
jobPrenom = Cells(i, 1).Value
jobSurname = Cells(i, 2).Value
For j = 1 To EmployeeCount
Sheets("EmpCon List").Select
prenom = Cells(j, 13).Value
surname = Cells(j, 14).Value
indexNo = Cells(j, 12).Value
badgeNumber = Cells(j, 15).Value
' Use UCase as sometimes the names are not always in lower/uppercase
If UCase(prenom) = UCase(jobPrenom) And UCase(surname) = UCase(jobSurname) Then
Sheets("All").Select
Cells(i, 16).Value = badgeNumber
Exit For
End If
Next j
Next i
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Not the solution (AFAIK), but I just wanted to show you how to cut down your code (and any potential pitfalls with .Select). This should do the same. Note how I created two worksheet variables, and then qualified the ranges with the sheet the info is coming from.
Sub BadgeNumberLookUp_No_Select()
Dim i As Integer, j As Integer
Dim empConWS As Worksheet, allWS As Worksheet
Set empConWS = Sheets("EmpCon List")
Set allWS = Sheets("All")
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Sheets("All").Select ' Job Number page
JobRows = Application.CountA(allWS.Range("A:A")) + 10 ' This number is 8000 and growing
'Sheets("EmpCon List").Select 'Employee / Contractors sheet
EmployeeCount = Application.CountA(empConWS.Range("M:M")) + 10 ' This number is about 450 and stable
For i = 1 To JobRows
'Sheets("All").Select
With allWS
jobPrenom = .Cells(i, 1).Value
jobSurname = .Cells(i, 2).Value
End with
For j = 1 To EmployeeCount
'Sheets("EmpCon List").Select
With empConWS
prenom = .Cells(j, 13).Value
surname = .Cells(j, 14).Value
indexNo = .Cells(j, 12).Value
badgeNumber = .Cells(j, 15).Value
End With
' Use UCase as sometimes the names are not always in lower/uppercase
If UCase(prenom) = UCase(jobPrenom) And UCase(surname) = UCase(jobSurname) Then
'Sheets("All").Select
allWS.Cells(i, 16).Value = badgeNumber
Exit For
End If
Next j
Next i
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Run this and see if the same errors occur for you.