Most efficient technique to check lots of cells contain an error? - vba

I have a spreadsheet which contains lots of function calls to request data. I am writing a function (in VBA) to check whether any of the cells contains an error value "#VALUE" etc.
At the moment I am iterating row by row, column by column and first checking if the cell contains a formula, then if it does, checking instr for "#VALUE", "#N/A" etc.
However, I was wondering whether it would be quicker simulating clicking a whole column in excel and then "ctrl + f" for a value... in VBA.
What would be the most efficient way? I am checking a sheet 27 columns x 1200 rows large.
EDIT Ive just realised there are some cells which have "#N/A" and this is because they do not contain a particular formula. I need to only search in cells which contain a particular formula.... is this possible?
EDIT2 I effectively need to record a macro which returns the resutls, exactly like "find all". I have used "find" and i can get a boolean, but "find all" doesnt record any VBA code....

You can use SpecialCells to return only cells containing errors.
Sub Demo()
Dim sh As Worksheet
Dim rng As Range, cl As Range
For Each sh In ActiveWorkbook.Worksheets
Set rng = Nothing
On Error Resume Next
Set rng = sh.UsedRange.SpecialCells(xlCellTypeFormulas, xlErrors)
On Error GoTo 0
If rng Is Nothing Then
Debug.Print "No Errors"
Else
For Each cl In rng
If cl.Formula Like "*" Then ' <-- replace * with your criteria
Debug.Print cl.Address
End If
Next
End If
Next
End Sub

Given you wanted the most efficient method you could try this approach which avoids a slow range loop
Loops through SpecialCells formulae chichi contain errors (as per the other solution)
Uses Find to detect specific formulae rather than a simple loop through every cell in (1)
This code uses the R1C1 method to feed into the Find so the code changes this Application setting if necessary (and then back at the end)
I suggest you record the formula you wish to find to then enter this in. The big advantage of R1C1 notation is that it is agnostic of actual row and column location.
For example in A1 notation a formula of
=SUM(A1:A4) in A5 would require a different search for SUM(B1:B4) inB5`
in R1C1 this is =SUM(R[-4]C:R[-1]C) in both cases
code
Sub Demo()
Dim ws As Worksheet
Dim rng1 As Range
Dim rng2 As Range
Dim rng3 As Range
Dim strAddress As String
Dim bRefSTyle
If Application.ReferenceStyle = xlA1 Then
Application.ReferenceStyle = xlR1C1
bRefSTyle = True
End If
For Each ws In ActiveWorkbook.Worksheets
Set rng1 = Nothing
On Error Resume Next
Set rng1 = ws.UsedRange.SpecialCells(xlCellTypeFormulas, xlErrors)
On Error GoTo 0
If rng1 Is Nothing Then
Debug.Print ws.Name & ": No Formulae errors"
Else
'search errors for particular formula
'this sample looks for a formula which SUMS the four cells directly above it
Set rng2 = rng1.Find("=SUM(R[-4]C:R[-1]C)", , xlFormulas, xlWhole)
If Not rng2 Is Nothing Then
strAddress = rng2.Address
Set rng3 = rng2
Do
Set rng2 = rng1.Find("=SUM(R[-4]C:R[-1]C)", rng2, xlFormulas, xlWhole)
Set rng3 = Union(rng2, rng3)
Loop While strAddress <> rng2.Address
Debug.Print ws.Name & ": " & rng3.Address
Else
Debug.Print ws.Name & ": error cells, but no formulae match"
End If
End If
Next
'restore styles if necessary
If bRefSTyle Then Application.ReferenceStyle = xlA1
End Sub

Related

Runtime error 13 when using Trim

I have this bit of VBA that I've used on many Excel workbooks without issues. On a particular workbook, I'm getting a Runtime Error '13': Type Mismatch error as soon as it gets to the Cell = Trim(Cell) part. What could be causing this? I've looked through the data, but can't find anything out of the ordinary that would be messing it up.
Sub TrimHS()
Application.Cursor = xlWait
Application.ScreenUpdating = False
Dim LastRow As Integer
LastRow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
Range("A83:G" & LastRow).Select
Dim rng As Range, Cell As Range
Set rng = Selection
For Each Cell In rng
Cell = Trim(Cell)
Next Cell
Application.Cursor = xlDefault
End Sub
You have an error on a particular workbook, thus the Trim() gives error 13, if you are trying to trim it. Make sure that you have no error before trimming:
For Each Cell In rng
If Not IsError(Cell) Then
Cell = Trim(Cell)
End If
Next Cell
To see what is wrong with the code, write debug.print Cell before the Trim line. It would start printing a lot at the console, the last line to print should look like this: Error 2007 or similar.
This will show the cell with the error in a MsgBox and its Worksheet:
For Each Cell In rng
If Not IsError(Cell) Then
Cell = Trim(Cell)
Else
MsgBox "Error on " & Cell.Address & " in " & Cell.Parent.Name
End If
Next Cell
Cell is of type Excel.Range and Trim outputs of type String
I believe cell.value=trim(cell.value) is what you'll need

VBA - Find all matches across multiple sheets

I am working on a macro that will search an entire workbook for various codes. These codes are all six digit numbers. Codes I wish to search for are input in column A of a sheet called "Master". If a code found on another sheet matches one in Master it's sheet name and cell will be pasted in column B next to it's match in Master. When successful the end result looks like this.
The code posted below works in certain cases, but fails quite often. Occasionally a run-time error will appear, or an error message with "400" and nothing else. When these errors occur the macro fills a row with matches for a blank value at the end of all the listed codes. This is obviously not an intended function.
I am at a loss regarding the above error. I have wondered if limiting the search range would help stability. All codes on other sheets are only found in column A, so searching for matches in all columns as is done currently is quite wasteful. Speed is secondary to stability however, I first want to eliminate all points of failure.
Sub MasterFill()
Dim rngCell As Range
Dim rngCellLoc As Range
Dim ws As Worksheet
Dim lngLstRow As Long
Dim lngLstCol As Long
Dim strSearch As String
Sheets("Master").Select
lngLstRowLoc = Sheets("Master").UsedRange.Rows.Count
Application.ScreenUpdating = False
For Each rngCellLoc In Range("A1:A" & lngLstRowLoc)
i = 1
For Each ws In Worksheets
If ws.Name = "Master" Then GoTo SkipMe
lngLstRow = ws.UsedRange.Rows.Count
lngLstCol = ws.UsedRange.Columns.Count
ws.Select
For Each rngCell In Range(Cells(2, 1), Cells(lngLstRow, lngLstCol))
If InStr(rngCell.Value, rngCellLoc) > 0 Then
If rngCellLoc.Offset(0, i).Value = "" Then
rngCellLoc.Offset(0, i).Value = ws.Name & " " & rngCell.Address
i = i + 1
End If
End If
Next
SkipMe:
Next ws
Next
Application.ScreenUpdating = True
Worksheets("Master").Activate
MsgBox "All done!"
End Sub
See if this doesn't expedite matters while correcting the logic.
Sub MasterFill()
Dim addr As String, fndCell As Range
Dim rngCellLoc As Range
Dim ws As Worksheet
Application.ScreenUpdating = False
With Worksheets("Master")
For Each rngCellLoc In .Range(.Cells(1, "A"), .Cells(.Rows.Count, "A").End(xlUp))
For Each ws In Worksheets
If LCase(ws.Name) <> "master" Then
With ws.Columns("A")
Set fndCell = .Find(what:=rngCellLoc.Value2, After:=.Cells(1), _
LookIn:=xlFormulas, LookAt:=xlPart, _
MatchCase:=False, SearchFormat:=False)
If Not fndCell Is Nothing Then
addr = fndCell.Address(0, 0)
Do
With rngCellLoc
.Cells(1, .Parent.Columns.Count).End(xlToLeft).Offset(0, 1) = _
Join(Array(ws.Name, fndCell.Address(0, 0)), Chr(32))
End With
Set fndCell = .FindNext(After:=fndCell)
Loop While addr <> fndCell.Address(0, 0)
End If
End With
End If
Next ws
Next
.Activate
End With
Application.ScreenUpdating = True
MsgBox "All done!"
End Sub
I've used LookAt:=xlPart in keeping with your use of InStr for criteria logic; if you are only interested in whole cell values change this to LookAt:=xlWhole.
I've restricted the search range to column A in each worksheet.
Previous results are not cleared before adding new results.
Your own error was due to the behavior where a zero length string (blank or vbNullString) is found within any other string when determined by Instr.

How to loop through only the non-empty cell in a column in an excel sheet via VBA?

According to this website.
I think this should work:
Dim cell As Range
For Each cell In xxxSheet.Range("B:B").SpecialCells(xlCellTypeFormulas, xlNumbers)
'Do sth.
Next
which does not work. Is there something missing?
This should be working solution:
For Each cell In xxxSheet.Range("B:B")
If Not IsEmpty(cell) Then
'do sth
End If
Next
Also, if you want to loop until last filled cell, you could use following:
xxxSheet.Range("B1:B" & Cells(Rows.Count, 2).End(xlUp).Row)
instead of
xxxSheet.Range("B:B")
It does not work, because you do not have formulas on column B. Put some formulas and some constants and try this:
Option Explicit
Public Sub TestMe()
Dim myCell As Range
Dim myRange As Range
Set myRange = Worksheets(1).Columns("B:B").SpecialCells(xlCellTypeFormulas, xlNumbers)
For Each myCell In myRange
Debug.Print myCell.Address
Next
Set myRange = Worksheets(1).Columns("B:B").SpecialCells(xlCellTypeConstants, xlNumbers)
For Each myCell In myRange
Debug.Print myCell.Address
Next
End Sub
The first loop would print the addresses of the formula cells, the second the addresses of the constants.
This is the ozgrid explanation about SpecialCells:
http://www.ozgrid.com/VBA/special-cells.htm
The problem is SpecialCells(xlCellTypeFormulas, xlNumbers) is returning only cells with formulas that make numbers (ie. =1+2).
To keep things efficient, you only need to check up to the last filled row
For Each cell In xxxSheet.Range("B1", Cells(Rows.Count, 2).End(xlUp))
If Not IsEmpty(cell) Then
'Do sth.
End If
Next
If you really want you can use SpecialCells() to have a range containing no blanks to loop through. If you only have formulas or only constants, you could use SpecialCells(xlFormulas) or SpecialCells(xlConstants) respectively, but for a more general use case you will have to do do a combination of the two.
Dim cell As Range
Dim searchRange As Range
' SpecialCells errors when there aren't cells instead of giving a useful value
On Error Resume Next
Set searchRange = xxxSheet.Range("B:B").SpecialCells(xlFormulas)
Set searchRange = xxxSheet.Range("B:B").SpecialCells(xlConstants)
Set searchRange = Union(xxxSheet.Range("B:B").SpecialCells(xlConstants), _
xxxSheet.Range("B:B").SpecialCells(xlFormulas))
On Error GoTo 0
If searchRange Is Not Nothing Then ' Only continue if no blanks
For Each cell In searchRange
'Do sth.
Next
End If

Find to cells in range with specific colors and add comments

I am trying to create a macro that will search a column of text (A:A) for a specific interior color. In this case the interior color is 55. Normally I'd create a range of A1:A101 but the data that is added changes daily so there may be more or less.
Essentially once the macro identifies the cells with the colors I want the macro to add a comment to the cell. Something simple like "Hello World!".
So far this is what I have:
Sub AddCommentBasedOnColor()
Dim rng As Range, cell As Range
Set rng = Range("G:G")
Application.ScreenUpdating = False
Application.Calculation = xlManual
For Each cell In rng
If cell.Interior.ColorIndex = 55 Then
If rng.Comment Is Nothing Then rng.AddComment
rng.Comment.Text "Possible Aux Stacking"
End
End If
Next cell
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
End Sub
The problem that I am running into is that when I am running the code, the comment portion does not work at all. No comments are made and for some reason I get a debug code but did not have one before. Not sure what I did that changed it.
Additionally, when I remove the commenting section of this code it does take some time to run, any assistance with shortening that length of time would be appreciated as well.
Your code has logical problems.
With rng.AddComment you try setting a comment to the whole column G as rng is the whole column G. This is not possible.
And your inner If statement works as follows:
...
If rng.Comment Is Nothing Then rng.AddComment
rng.Comment.Text "Possible Aux Stacking"
End
...
If rng.Comment Is Nothing Then rng.AddComment. Here the If ends. The next program row is processing ever without additional conditions and the End then ends the Sub at this point.
To shortening the processing time you have not to run over all rows in column G. This is possible by calculation the last used row. How to do this differs on how you define the last used row. Since you are working with the cell's interior, I have defined the last used row as the last row having cells with not default content of empty cells.
Sub AddCommentBasedOnColor()
Dim rng As Range, cell As Range, lastUsedRow As Long
With ActiveSheet
lastUsedRow = .UsedRange.Rows(.UsedRange.Rows.Count).Row
Set rng = .Range("G1:G" & lastUsedRow)
For Each cell In rng
If cell.Interior.ColorIndex = 55 Then
If cell.Comment Is Nothing Then
cell.AddComment
cell.Comment.Text "Possible Aux Stacking"
End If
End If
Next cell
End With
End Sub
You can use Find rather than loop through each cell:
Sub AddCommentBasedOnColor()
Dim rng1 As Range
Dim rng2 As Range
Dim strFirst As String
Application.FindFormat.Interior.ColorIndex = 55
Set rng1 = Columns("G:G").Find(What:="", SearchDirection:=xlNext, SearchFormat:=True)
If Not rng1 Is Nothing Then
strFirst = rng1.Address
Set rng2 = rng1
Do
Set rng2 = Columns("G:G").Find(What:="", After:=rng2, SearchDirection:=xlNext, SearchFormat:=True)
If rng2.Comment Is Nothing Then
rng2.AddComment
rng2.Comment.Text "Possible Aux Stacking"
End If
Loop Until rng2.Address = strFirst
End If
End Sub

Last not empty cell (column) in the given row; Excel VBA

I have an excel sheet in which I need to find the last non empty cell in a specific row.
How do I do this?
The below will select this for me, but it will select the first not empty cell, I need the last not empty cell in the row #29.
Worksheets("DTCs").Range("A29").End(xlToRight).Select
I have expanded on my comment above to provide solutions that
do not use Select
cater for the last cell in row 1 being used
cater for the entire row being empty
cater for the entire row being full
The Find method in the second code is a far more direct method of establishing the first non-blank cell
This line Set rng1 = ws.Rows(1).Find("*", ws.[a1], xlValues, , xlByColumns, xlPrevious)
says, start in cell A1 of Sheet "DTCa" then look backwards (ie from the last cell in row 1) in row1 by column looking for anything (the *). This method either find the last non blank or returns Nothing , ie an empty row
using xltoLeft with specific checks
Sub Method1()
Dim ws As Worksheet
Dim rng1 As Range
Set ws = Sheets("DTCs")
If ws.Cells(1, Columns.Count) = vbNullString Then
Set rng1 = ws.Cells(1, Columns.Count).End(xlToLeft)
If rng1.Column <> 1 Then
'return last used cell
MsgBox "rng1 contains " & rng1.Address(0, 0)
Else
If ws.[a1] = vbNullString Then
MsgBox ws.Name & " row1 is completely empty", vbCritical
Else
'true last used cell is A1
MsgBox "rng1 contains " & rng1.Address(0, 0)
End If
End If
Else
'last cell is non-blank
MsgBox ws.Cells(1, Columns.Count) & " contains a value", vbCritical
End If
End Sub
recommended
Sub Method2()
Dim ws As Worksheet
Dim rng1 As Range
Set ws = Sheets("DTCs")
Set rng1 = ws.Rows(1).Find("*", ws.[a1], xlFormulas, , xlByColumns, xlPrevious)
If Not rng1 Is Nothing Then
MsgBox "rng1 contains " & rng1.Address(0, 0)
Else
MsgBox ws.Name & " row1 is completely empty", vbCritical
End If
End Sub
I think it might work just search from the other direction, so something like:
Worksheets("DTCs").Range("IV29").End(xlToLeft).Select
Though maybe the IV would need to be changed to something else depending on the version of Excel (this seems to work in 2003).