VBA to delete entire row based on cell value - vba

I'm experiencing some issues getting the provided VBA code working and would appreciate any assistance.
I have two Workbooks (1) is a monthly report I receive that has multiple worksheets, Worksheet "host_scan_data" contains the source of the information I will need to work with. The other Workbook (2) is where I will store all consolidated date month over month.
How I'm trying to accomplish this task:
1. launch workbook #2
2. click a button that has the following VBA code assigned to (see below)
3. browse and select my monthly report (workbook #1)
4. specify the worksheet tab in workbook #2 where i'd like to store this consolidate information
5. prompt user to validate worksheet tab where data will be stored
Based on the responses above the macro will then analyze Column K within the "host_scan_data" Sheet of the Workbook (1), and I would like for it to remove all rows where Column k contains a "0" (note the only values i'm concerned about are 4,3,2,1). Once that action is complete i'd like for the macro to copy the consolidated list of entry's over to the location specified in step #4 above.
I've tried this with a few variations of code and other solutions appear to work fine when the "host_scan_data" Sheet contains <4,000 rows however once I exceed that number (give or take) excel becomes unresponsive. Ideally this solution will need to handle approx 150,000+ rows.
Here is the code i'm currently using, when i execute it errors out at ".Sort .Columns(cl + 1), Header:=xlYes":
The Code I Have so far:
Sub Import()
Dim strAnswer
Dim itAnswer As String
Dim OpenFileName As String
Dim wb As Workbook
Dim db As Workbook
Dim Avals As Variant, X As Variant
Dim i As Long, LR As Long
'Optimize Code
Call OptimizeCode_Begin
'Select and Open workbook
OpenFileName = Application.GetOpenFilename("*.xlsx,")
If OpenFileName = "False" Then Exit Sub
Set wb = Workbooks.Open(OpenFileName, UpdateLinks:=0)
Set db = ThisWorkbook
'Provide Sheet Input
strAnswer = InputBox("Please enter name of worksheet where Nessus data will be imported:", "Import Name")
If strAnswer = "" Then
MsgBox "You must enter a valid name. Exiting now..."
wb.Close
Exit Sub
Else
Response = MsgBox(strAnswer, vbYesNo + vbCritical + vbDefaultButton2, "Is this Correct?")
If Response = vbNo Then
MsgBox "Got it, you made a mistake. Exiting now..."
wb.Close
Exit Sub
Else: MsgBox "Importing Now!"
End If
End If
wb.Sheets("host_scan_data").Activate
Dim rs, cl, Q()
Dim arr1, j, C, s As Long
Dim t As String: t = "4"
Dim u As String: u = "3"
Dim v As String: v = "2"
Dim w As String: w = "1"
If Cells(1) = "" Then Cells(1) = Chr(2)
'Application.Calculation = xlManual
rs = wb.Sheets("host_scan_data").Cells.Find("*", , , , , xlByRows, xlPrevious).Row
cl = wb.Sheets("host_scan_data").Cells.Find("*", , , , , xlByColumns, xlPrevious).Column
ReDim Q(1 To rs, 1 To 1)
arr1 = wb.Sheets("host_scan_data").Cells(1, "k").Resize(rs)
For j = 1 To rs
C = arr1(j, 1)
If (C <> t) * (C <> u) * (C <> v) * (C <> w) Then Q(j, 1) = 1: s = s + 1
Next j
If s > 0 Then
With Cells(1).Resize(rs, cl + 1)
.Columns(cl + 1) = Q
.Sort .Columns(cl + 1), Header:=xlYes
.Cells(cl + 1).Resize(s).EntireRow.Delete
End With
End If
countNum = (Application.CountA(Range("B:B"))) - 1
MsgBox (countNum & " Rows being imported now!")
countNum = countNum + 2
db.Sheets(strAnswer).Range("A3:A" & countNum).value = wb.Sheets("host_scan_data").Range("B3:B" & countNum).value
db.Sheets(strAnswer).Range("B3:B" & countNum).value = wb.Sheets("host_scan_data").Range("K3:K" & countNum).value
db.Sheets(strAnswer).Range("C3:C" & countNum).value = wb.Sheets("host_scan_data").Range("H3:H" & countNum).value
db.Sheets(strAnswer).Range("D3:D" & countNum).value = wb.Sheets("host_scan_data").Range("M3:M" & countNum).value
db.Sheets(strAnswer).Range("E3:E" & countNum).value = wb.Sheets("host_scan_data").Range("L3:L" & countNum).value
db.Sheets(strAnswer).Range("F3:F" & countNum).value = wb.Sheets("host_scan_data").Range("O3:O" & countNum).value
db.Sheets(strAnswer).Range("G3:G" & countNum).value = wb.Sheets("host_scan_data").Range("G3:G" & countNum).value
db.Sheets(strAnswer).Range("K3:K" & countNum).value = wb.Sheets("host_scan_data").Range("X3:X" & countNum).value
MsgBox ("Done")
'Close nessus file
wb.Close SaveChanges:=False
'Else
'MsgBox "You must enter 1 or 2 only. Exiting now..."
'wb.Close
'Exit Sub
'End If
Sheets(strAnswer).Select
'Optimize Code
Call OptimizeCode_End
End Sub

So here is what may be happening.
If the row you are deleting has data used, in a formula somewhere else, that formula is going to recalculate on every iteration of the row delete.
I had this problem with a data set which has many Vlookup functions pulling data.
here is what I did and it take a few seconds rather than 30min
Sub removeLines()
Dim i As Long
Dim celltxt As String
Dim EOF As Boolean
Dim rangesize As Long
EOF = False
i = 1
'My data has "End of File" at the end so I check for that
' Though it would be better to used usedRange
While Not (EOF)
celltxt = ActiveSheet.Cells(i, 1).Text
If InStr(1, celltxt, "end", VbCompareMethod.vbTextCompare) > 0 Then
EOF = True 'if we reach the "end Of file" then exit
' so I clear a cell that has no influence on any functions thus
' it executes quickly
ElseIf InStr(1, celltxt, "J") <> 1 Then
Cells(i, 1).Clear
End If
i = i + 1
Wend
' once all the rows to be deleted are marked with the cleared cell
' I use the specialCells to select and delete all the rows at once
' so that the dependent formula are only recalculated once
Columns("A:A").Select
Selection.SpecialCells(xlCellTypeBlanks).Select
Selection.EntireRow.Delete
End Sub
hope this helps and that it is read able

I tried a little different approach by using AutoFilter and i'm seeing a high success rate on my larger lists however there still is one issue. With the code below i was able to parse through 67k+ rows and filter/delete any row contains a "0" in my column K (this takes approx 276 seconds to complete), after the code filters and deletes the rows with zeros it clears any existing filters then is to copy the remaining data into my Workbook #2 (this is approx 7k rows) however it is consistently only copying 17 rows of data into my workbook #2, it just seems to stops and i have no idea why. Also, while 4.5 mins to complete the consolidation could be acceptable does anyone have any ideas on how to speed this up?
Sub Import()
Dim strAnswer
Dim itAnswer As String
Dim OpenFileName As String
Dim wb As Workbook
Dim db As Workbook
Dim Avals As Variant, X As Variant
Dim i As Long
Dim FileLastRow As Long
Dim t As Single
Dim SevRng As Range
t = Timer
'Optimize Code
Call OptimizeCode_Begin
'Select and Open workbook
OpenFileName = Application.GetOpenFilename("*.xlsx,")
If OpenFileName = "False" Then Exit Sub
Set wb = Workbooks.Open(OpenFileName, UpdateLinks:=0)
Set db = ThisWorkbook
'Provide Sheet Input
strAnswer = InputBox("Please enter name of worksheet where Nessus data will be imported:", "Import Name")
If strAnswer = "" Then
MsgBox "You must enter a valid name. Exiting now..."
wb.Close
Exit Sub
Else
Response = MsgBox(strAnswer, vbYesNo + vbCritical + vbDefaultButton2, "Is this Correct?")
If Response = vbNo Then
MsgBox "Got it, you made a mistake. Exiting now..."
wb.Close
Exit Sub
Else: MsgBox "Importing Now!"
End If
End If
FileLastRow = wb.Sheets("host_scan_data").Range("K" & Rows.Count).End(xlUp).Row
Set SevRng = wb.Sheets("host_scan_data").Range("K2:K" & FileLastRow)
Application.DisplayAlerts = False
With SevRng
.AutoFilter Field:=11, Criteria1:="0"
.Offset(1, 0).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Rows.Delete
.Cells.AutoFilter
End With
Application.DisplayAlerts = True
MsgBox "Consolidated in " & Timer - t & " seconds."
countNum = (Application.CountA(Range("B:B"))) - 1
MsgBox (countNum & " Rows being imported now!")
countNum = countNum + 2
db.Sheets(strAnswer).Range("A3:A" & countNum).value = wb.Sheets("host_scan_data").Range("B3:B" & countNum).value
db.Sheets(strAnswer).Range("B3:B" & countNum).value = wb.Sheets("host_scan_data").Range("K3:K" & countNum).value
db.Sheets(strAnswer).Range("C3:C" & countNum).value = wb.Sheets("host_scan_data").Range("H3:H" & countNum).value
db.Sheets(strAnswer).Range("D3:D" & countNum).value = wb.Sheets("host_scan_data").Range("M3:M" & countNum).value
db.Sheets(strAnswer).Range("E3:E" & countNum).value = wb.Sheets("host_scan_data").Range("L3:L" & countNum).value
db.Sheets(strAnswer).Range("F3:F" & countNum).value = wb.Sheets("host_scan_data").Range("O3:O" & countNum).value
db.Sheets(strAnswer).Range("G3:G" & countNum).value = wb.Sheets("host_scan_data").Range("G3:G" & countNum).value
db.Sheets(strAnswer).Range("K3:K" & countNum).value = wb.Sheets("host_scan_data").Range("X3:X" & countNum).value
MsgBox ("Done")
'Close nessus file
wb.Close SaveChanges:=False
Sheets(strAnswer).Select
'Optimize Code
Call OptimizeCode_End
End Sub

Does your
"MsgBox (countNum & " Rows being imported now!")"
return the correct number of rows?
CountA will stop counting at the first empty cell.
Try instread:
countNum = ActiveSheet.UsedRange.Rows.Count

Related

getting my range loop to disregard the header row?

I have a simple for loop that counts the amount of visible rows after an autofilter. I've done research and the consensus seems to be something like .Range.SpecialCells(xlCellTypeVisible).Rows.count - 1 should work, but it keeps counting the header row.
On Error Resume Next
termsfound = 0
For Each mycell In tgtws.Range.SpecialCells(xlCellTypeVisible).Rows.count - 1
If IsError(mycell) = False Then
termsfound = termsfound + 1
End If
Next
If termsfound > 0 Then
MsgBox "Found " & termsfound & " term(s).", vbOKOnly, "Results"
Else
TgtWS.Rows(1).EntireRow.AutoFilter
TgtWS.Range("A2").Select
MsgBox "No terms found"
End If
any idea why it's not working properly?
whole code:
Sub LOBEligibilityTermCheck()
Dim SrcWB As Workbook
Dim SrcWS As Worksheet
Dim TgtWS As Worksheet
Workbooks.Open ("M:\Final Terms.xlsx")
Workbooks.Open ("M:\daily-report.xlsx")
Set SrcWB = Workbooks("Final Terms.xlsx")
Set TgtWB = Workbooks("daily-report.xlsx")
Set SrcWS = SrcWB.Sheets("Sheet1")
Set TgtWS = TgtWB.Sheets(1)
Application.ScreenUpdating = False
If WorksheetIsOpen("Final Terms.xlsx", "Sheet1") = False Then
MsgBox "This macro requires the term file to be open prior to running." & vbNewLine & vbNewLine _
& "The file name MUST be 'Final Terms .xlsx' and the list MUST be in a worksheet (tab) titled 'Sheet1'." _
& vbNewLine & vbNewLine & "Please open the file and run the macro again.", vbOKOnly, "Error"
Exit Sub
End If
lastCell = TgtWS.Range("A" & TgtWS.Rows.Count).End(xlUp).Row + 1
TgtWS.Rows(1).EntireRow.Delete
TgtWS.Columns("E").Insert
TgtWS.Range("E1") = "Social Security Number"
TgtWS.Range("E2").FormulaR1C1 = "=IF(RC[-2]="""",RC[-1],RC[-2])& """""
TgtWS.Range("e2").AutoFill Destination:=TgtWS.Range("e2:e" & lastCell)
TgtWS.Range("e:e").Copy
TgtWS.Range("e:e").PasteSpecial xlPasteValues
TgtWS.Range("C:D").EntireColumn.Delete
TgtWS.Range("A1:A" & Range("AP" & Rows.Count).End(xlUp).Row).AutoFilter Field:=15, Criteria1:=">=" & CLng(Date - 2)
lastrowlob = LastRowIndex(TgtWS, 1)
TgtWS.Columns("D").Insert
TgtWS.Cells(1, 4) = "Unique Identifier"
TgtWS.Range(Cells(2, 4), Cells(lastrowlob, 4)).SpecialCells(xlCellTypeVisible).FormulaR1C1 = "=trim(rc[-3]&right(rc[-1],4))"
TgtWS.Columns("E").Insert
TgtWS.Cells(1, 5) = "Eligibility Lookup"
TgtWS.Range(Cells(2, 5), Cells(lastrowlob, 5)).SpecialCells(xlCellTypeVisible).FormulaR1C1 = "=IFNA(INDEX('[Final Terms.xlsx]Sheet1'!C13,MATCH(RC[-1],'[Final Terms.xlsx]Sheet1'!C13,0)),"""")"
TgtWS.Rows(1).EntireRow.AutoFilter
TgtWS.Range("E:E").Copy
TgtWS.Range("E:E").PasteSpecial xlPasteValues
TgtWS.Range("$A$1:$AO$" & lastrowlob).AutoFilter Field:=5, Criteria1:="<>", Operator:=xlAnd
TgtWS.Range("A2").Select
On Error Resume Next
termsfound = 0
For Each mycell In tgtws.Range.SpecialCells(xlCellTypeVisible).Rows.count - 1
If IsError(mycell) = False Then
termsfound = termsfound + 1
End If
Next
If termsfound > 0 Then
MsgBox "Found " & termsfound & " term(s).", vbOKOnly, "Results"
Else
TgtWS.Rows(1).EntireRow.AutoFilter
TgtWS.Range("A2").Select
MsgBox "No terms found"
End If
SrcWB.Close savechanges = False
Application.ScreenUpdating = True
End Sub
You are mixing up For and For Each. For works with numbers. For Each does exactly what is says - for each entry in the object, it will do what you want. In your situation, you can work with row numbers, but I would go for For Each. The cheap solution here is to test if it the first run and skip the action, but that's not good code. Do like this instead:
Dim TargetRange As Range
Set TargetRange = tgtws.Range.SpecialCells(xlCellTypeVisible)
Set TargetRange = TargetRange.Offset(1) ' move it down one row
Set TargetRange = TargetRange.Resize(TargetRange.Rows.Count - 1) ' make it one row shorter
For Each mycell In TargetRange
'...
Next
I have copied your range definition and put it in a variable. Then the range is moved one step down, and then resized. Put a TargetRange.Select in your code at a suitable location - it's a quick way to check the result while debugging.

Why does Application.Match behave inconsistently when run multiple times on the same data?

The background:
I have a workbook, Outline.xlsm, with a five-level hierarchy. In the first worksheet (WS1), the first three levels are described the first two columns, while the next two levels each have their own set of two columns:
In the second worksheet (WS2), there is no level 3, but everything else is the same. All cells are formatted as text.
I have some code that splits out each first-level section ("General thing") into its own workbook to allow users to make changes to the descriptions (and some other fields off to the right). The code in question then goes out and gets those new descriptions from each file and matches them to the ID number. Here is a sanitized version:
Option Explicit
Sub GatherData()
'Set up for speed
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
'Get files to be processed
Dim DataFolder As String
Dim DataFile As String
DataFolder = "\\SomeNetworkLocation"
DataFile = Dir(DataFolder & "\GeneralThing*.xlsx")
'Define ranges to search
Dim WS1_L1Rng As Range
Dim L2rng As Range
Dim L3rng As Range
Set WS1_L1Rng = Worksheets("WS1").Range("A2", "A" & Range("N2").End(xlDown).Row)
Set L2rng = Worksheets("WS1").Range("C2", "C" & Range("N2").End(xlDown).Row)
Set L3rng = Worksheets("WS1").Range("E2", "E" & Range("N2").End(xlDown).Row)
Dim WS2_L1Rng As Range
Dim WS2_L2Rng As Range
Set WS2_L1Rng = Worksheets("WS2").Range("A2", "A" & Range("K2").End(xlDown).Row)
Set WS2_L2Rng = Worksheets("WS2").Range("C2", "C" & Range("K2").End(xlDown).Row)
Dim MatchPos As Variant
Dim WS1_SearchRng As Range
Dim WS2_SearchRng As Range
Dim Cell As Range
'Find and copy data
Do While DataFile <> ""
Workbooks.Open Filename:=DataFolder & "\" & DataFile
With Workbooks(DataFile).Worksheets("WS1")
Set WS1_SearchRng = .Range("A2:" & "A" & .Range("A" & .Rows.Count).End(xlUp).Row & ",C2:" & "C" & .Range("C" & .Rows.Count).End(xlUp).Row & ",E2:" & "E" & .Range("E" & .Rows.Count).End(xlUp).Row)
End With
For Each Cell In WS1_SearchRng
If IsNumeric(Left(Cell.Value2, 2)) Then
Select Case Cell.Rows.OutlineLevel
Case Is < 4
MatchPos = Application.Match(Cell.Value2, WS1_L1Rng, 0)
Case 4
MatchPos = Application.Match(Cell.Value2, L2rng, 0)
Case 5
MatchPos = Application.Match(Cell.Value2, L3rng, 0)
End Select
If IsError(MatchPos) Then
Debug.Print "WS1 " & Cell.Value2
Else
MatchPos = MatchPos + 1
Workbooks(DataFile).Worksheets("WS1").Range("A" & Cell.Row, "L" & Cell.Row).Copy Destination:=Workbooks("Outline.xlsm").Worksheets("WS1").Range("A" & MatchPos, "L" & MatchPos)
End If
End If
DoEvents
Next Cell
If Workbooks(DataFile).Worksheets.Count > 1 Then
With Workbooks(DataFile).Worksheets("WS2")
Set WS2_SearchRng = .Range("A2:" & "A" & .Range("A" & .Rows.Count).End(xlUp).Row & ",C2:" & "C" & .Range("C" & .Rows.Count).End(xlUp).Row)
End With
For Each Cell In WS2_SearchRng
If IsNumeric(Left(Cell.Value2, 2)) Then
Select Case Cell.Rows.OutlineLevel
Case Is < 4
MatchPos = Application.Match(Cell.Value2, WS2_L1Rng, 0)
Case 4
MatchPos = Application.Match(Cell.Value2, WS2_L2Rng, 0)
End Select
If IsError(MatchPos) Then
Debug.Print "WS2 " & Cell.Value2
Else
MatchPos = MatchPos + 1
Workbooks(DataFile).Worksheets("WS2").Range("A" & Cell.Row, "I" & Cell.Row).Copy Destination:=Workbooks("Outline.xlsm").Worksheets("WS2").Range("A" & MatchPos, "I" & MatchPos)
End If
End If
DoEvents
Next Cell
End If
With Workbooks(DataFile)
.Save
.Close
End With
DataFile = Dir
Loop
'Return to regular configuration
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
The problem:
Often, when I go to run this code, Application.Match throws an error when it tries to match to anything in WS2. It usually works fine if I just kill the execution and start over on the same data (sometimes it takes a few tries). Very rarely, it can't find anything in WS1 either; again, if I simply restart the execution it usually works just fine. Sometimes everything works great on the first try. Why does it not behave consistently?
Watch for implicit references to the active workbook/worksheet; what workbook/worksheet these instructions are referring to at run-time will depend on whatever workbook/worksheet is active at that time, and this is often responsible for such errors.
You can use Rubberduck (an open-source VBIDE add-in project I manage) to easily locate them for you (and other potential code issues).
Range("N2") in Worksheets("WS1").Range("A2", "A" & Range("N2").End(xlDown).Row) would be one. Worksheets used unqualified with a Workbook object would be another.
The solution is to explicitly qualify them with a Workbook or Worksheet object reference.

VBA code to protect all worksheets contingent on response

To delete a name and correlating data in the same row on each sheet of my workbook, users need to highlight a name and click a button on the first worksheet. A confirmation window then pops up asking if they are sure. If they click NO, everything remains protected and works fine. If they click YES, all worksheets are unprotected, and a second confirmation window appears - If YES is clicked the second time, the data is deleted from each worksheet and everything is protected after deletion. HOWEVER, if NO is clicked the second time, I can't get my code to then protect everything prior to exiting the sub.
Any help is appreciated, as well as suggestions for resources to help be become more proficient on my own. :)
Here is the code:
Sub DeleteRow()
'this macro deletes the row for a selected patient from worksheet of selected month and all months after that
'variables
Dim PatientName As String, PatientRow As Long, w As Long
Dim pRow As Long, lRow As Long, LookUpRng As Range, answer As Long
Dim rArray() As Variant, sArray As Variant
ReDim rArray(0)
ReDim sArray(0)
With ActiveSheet
ActiveSheet.Unprotect "arafluid"
PatientName = .Range("d" & ActiveCell.Row)
PatientRow = ActiveCell.Row
.Rows(PatientRow).Interior.ColorIndex = 4
'check that user want has selected correct patient
answer = MsgBox("Do you want to permanently remove patient " & vbCr & vbCr & _
PatientName & " from ALL months in this workbook?", vbYesNo, "Confirmation")
.Rows(PatientRow).Interior.ColorIndex = -4142
If answer = vbNo Then ActiveSheet.Protect "arafluid"
If answer <> vbYes Then Exit Sub
'check that it is safe to delete rows in future sheets
For w = Worksheets.Count To ActiveSheet.Index Step -1
With Sheets(w)
Sheets(w).Unprotect "arafluid"
pRow = 0
lRow = .Range("d10").CurrentRegion.Rows.Count + 9
Set LookUpRng = .Range("d10" & ":d" & lRow)
On Error Resume Next
pRow = Application.WorksheetFunction.Match(PatientName, LookUpRng, 0) + 9
If Err.Number <> 0 Then
Trail = Trail & vbCr & " " & .Name & " Not Found!"
Else
Trail = Trail & vbCr & " " & .Name & " ok"
' add value on the end of the arrays
ReDim Preserve rArray(UBound(rArray) + 1) As Variant
ReDim Preserve sArray(UBound(sArray) + 1) As Variant
rArray(UBound(rArray)) = pRow
sArray(UBound(sArray)) = w
End If
On Error GoTo 0
End With
Next w
'check that user still wants to delete
answer = MsgBox("Once deleted, the information cannot be recovered. Click YES to permanently remove: " & vbCr & vbCr & _
PatientName & vbCr, vbYesNo, "Are you sure?")
If answer <> vbYes Then Exit Sub
If answer <> vbNo Then
For a = Worksheets.Count To ActiveSheet.Index Step -1
Sheets(a).Protect "arafluid"
Next a
End If
'delete rows for selected patient
For d = 1 To UBound(sArray)
Sheets(sArray(d)).Rows(rArray(d)).EntireRow.Delete
Next d
End With
'loop through all sheets in the workbook.
For w = 1 To Sheets.Count
Sheets(w).Protect "arafluid"
Next w
End Sub
You are exiting the Sub if the user said "no". Change these lines after the second MessageBox:
answer = MsgBox("Once deleted, the information cannot be recovered. Click YES to permanently remove: " & vbCr & vbCr & _
PatientName & vbCr, vbYesNo, "Are you sure?")
If answer = vbNo Then 'This will test if user said "No" and will protect the sheets
For a = Worksheets.Count To ActiveSheet.Index Step -1
Sheets(a).Protect "arafluid"
Next a
Exit Sub
End If
As a note, after the first MsgBox you have the same situation with two If statements for the same thing, you could simplify them as:
If answer = vbNo Then
ActiveSheet.Protect "arafluid"
Exit Sub
End If

Excel VBA: message box to show list of errors at the end

I am new to VBA and I would really appreciate your help with the following. The code below searchers for blanks in Column A, highlights them, then it shows a message every time a cell has blanks with the cell location (e.g."No Value, in $A$1"). I copy these cells locations in another tab called "Results".
I need help with the following. I want to somehow for the message to show once with the list of cells that have blanks and their location. I do not want the message to pop out every time it finds a blank cell (I could have hundreds of blank cells in a file and clicking Ok for each one is not practical). Then that list of values will be copied in my "Results" sheet.
Here is the code I currently have:
Sub CeldasinInfo()
Dim i As Long, r As Range, coltoSearch As String
coltoSearch = "A"
For i = 1 To Range(coltoSearch & Rows.Count).End(xlUp).Row
Set r = Range(coltoSearch & i)
If Len(r.Value) = 0 Then
r.Interior.ColorIndex = 3 ' Red
r.Select
MsgBox "No Value, in " & r.Address
Sheets("Results").Range("A" & Sheets("Results").Range("A" & Rows.Count).End(xlUp).Row).Offset(1, 0).Formula = r.Address
End If
Next
End Sub
Thank so much in advance. I would really appreciate your help with this.
Something like this should work for you:
Sub CeldasinInfo()
Dim i As Long, r As Range, coltoSearch As String
Dim Result as String
Dim ErrCount as integer
ErrCount = 0
coltoSearch = "A"
coltoSearch = Range("1:1").find(What:="Hours", LookIn:=xlValues, LookAt:=xlWhole).Column
Result = "No Value in:" & vbcrlf
For i = 1 To Range(coltoSearch & Rows.Count).End(xlUp).Row
Set r = Range(coltoSearch & i)
If Len(r.Value) = 0 Then
r.Interior.ColorIndex = 3 ' Red
r.Select
' MsgBox "No Value, in " & r.Address
Result = Result & r.Address & vbcrlf
ErrCount = ErrCount + 1
if ErrCount Mod 10 = 0 then 'change to 15 or 20 or whatever works well
MsgBox Result
Result = "No Value in:" & vbcrlf
End If
Sheets("Results").Range("A" & Sheets("Results").Range("A" & Rows.Count).End(xlUp).Row).Offset(1, 0).Formula = r.Address
End If
Next
If ErrCount > 0 then
MsgBox "There were " & ErrCount & " errors detected." & vbcrlf & result
else
MsgBox "No errors detected"
End If
End Sub
This will give you each address on a separate line in the MsgBox. If there are hundreds of errors likely, this will result in a very long MsgBox output, and I'm not sure how it will handle that. You may need to add in a counter and display the message every 10, 15 or 20 errors for a better looking output.
If your cells are truly blank you can avoid a range loop and use SpecialCells
Sub CeldasinInfo()
Dim rng1 As Range
Dim coltoSearch As String
coltoSearch = "A"
On Error Resume Next
Set rng1 = Columns(coltoSearch).SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If rng1 Is Nothing Then Exit Sub
rng1.Interior.ColorIndex = 3 ' Red
MsgBox "No Value, in " & rng1.Address
End Sub

Using VLookup in a macro

I'm new to VBA but I'm hooked! I've created a workbook that tracks overtime in 2 week blocks with one 2-week block per worksheet. The macro I'm trying to debug is designed to carry any changes made in a worksheet over to following worksheets. The trick is that the data in one row may be in a different row in following worksheets so I trying to use VLookup in a macro to keep it accurate.
Sub CarryForward()
Dim Answer As String
Answer = MsgBox("This should only be used for a PERMANENT crew change." & vbNewLine & "If you are adding a new person to the list," & vbNewLine & "please use the Re-Sort function." & vbNewLine & "Do you want to continue?", vbExclamation + vbYesNo, "Caution!")
If Answer = vbNo Then
Exit Sub
End If
Application.ScreenUpdating = False
Dim ActiveWorksheet As String
ActiveWorksheet = ActiveSheet.Name
For i = (ActiveSheet.Index + 1) To Sheets("DATA").Index - 1
For x = 5 To 25
Dim a As String
Dim b As String
a = "B" & x
b = "C" & x
ActiveSheet.Range(b).Value = Application.WorksheetFunction.VLookup(a, Sheets(ActiveWorksheet).Range("B5:C25"), 2, False)
Next x
Range("A3").Select
Next i
Sheets(ActiveWorksheet).Select
Application.CutCopyMode = False
Range("A3").Select
Application.ScreenUpdating = True
End Sub
I'm pretty sure it's just a syntax error in the VLookup line of code. A lot of the help posted comes close to what I'm looking for, it just doesn't get me over the finish line.
Any help would be appreciated!
It is a little unclear what you are trying to do, but reading between the lines I think
you want to lookup the value contained in cell named by a?
and put the result on sheet index i?
Also, there is a lot of opportunity to improve your code: see imbedded comments below
Sub CarryForward()
Dim Answer As VbMsgBoxResult ' <-- Correct Datatype
Answer = MsgBox("This should only be used for a PERMANENT crew change." & vbNewLine & _
"If you are adding a new person to the list," & vbNewLine & _
"please use the Re-Sort function." & vbNewLine & _
"Do you want to continue?", _
vbExclamation + vbYesNo, "Caution!")
If Answer = vbNo Then
Exit Sub
End If
Application.ScreenUpdating = False
' Dim ActiveWorksheet As String <-- Don't need this
'ActiveWorksheet = ActiveSheet.Name <-- use object variables
Dim wbActive As Workbook ' <-- don't select, use variables for sheet objects
Dim shActive As Worksheet
Set wbActive = ActiveWorkbook
Set shActive = ActiveSheet
'Dim a As String ' <-- no point in putting these inside the loop in VBA. And don't need these anyway
'Dim b As String
Dim SearchRange As Range
Set SearchRange = shActive.Range("B5:C25") ' <-- Use variable to hold range
Dim shDest As Worksheet
Dim i As Long, x As Long '<-- dim all your variables
For i = (shActive.Index + 1) To wbActive.Worksheets("DATA").Index - 1 ' <-- qualify references
Set shDest = wbActive.Sheets(i)
For x = 5 To 25
'a = "B" & x <-- no need to create cell names
'b = "C" & x
' I think you want to lookup the value contained in cell named by a?
' and put the result on sheet index i?
' Note: if value is not found, this will return N/A. Add an error handler
wbActive.Sheets(i).Cells(x, 3).Value = Application.VLookup(shActive.Cells(x, 2).Value, SearchRange, 2, False)
Next x
'Range("A3").Select
Next i
'Sheets(ActiveWorksheet).Select ,-- don't need these
'Application.CutCopyMode = False
'Range("A3").Select
Application.ScreenUpdating = True
End Sub
I suspect you would want to replace the vlookup statement to be something like
Application.WorksheetFunction.VLookup(ActiveWorksheet.Range(a).value, ActiveWorksheet.Range("B5:C25"), 2, False)
at the moment it looks like you're just doing a vlookup against some strings B5, B6, B7 etc instead of values in those cells