1

I have a macro that does work, it's just really slow when there is a lot of data and I'm hoping that someone on here can help me to speed it up.

When my VBA does is check the columns of a sheet for the value "NULL" and if it's there it clears that cell. Here's the code:

Sub RemoveNullColumn()
    Dim c, count, r, lc, FirstCell
    Application.ScreenUpdating = False
    count = 0
    r = ActiveCell.row      'lets you choose where you want to start even if it is not at "A1"
    c = ActiveCell.Column   'lets you choose where you want to start even if it is not at "A1"
    c = GetLetterFromNumber(c)  'Gets the column letter from the number provided above
    FirstCell = c & r       'sets the cell that you selected to start in so that you will end thereafter removing all the NULL

    lc = ActiveSheet.Cells(1, Columns.count).End(xlToLeft).Column   'Finding the last used column
    For H = ActiveCell.Column To lc Step 1      'Starts with where you selected a cell and moves right to the last column
        For x = 1 To Range(c & Rows.count).End(xlUp).row Step 1     'Starts with the first row and moves through the last row
            count = count + 1
            If Range(c & x).Value = "NULL" Then 'Checks the contents fo the cell to see if it is "NULL"
                Range(c & x).Clear
            End If
            If count = 1000 Then    'This was used testing but is not seen with the ScreenUpdating set to false
                Range(c & x).Select
                count = 1
            End If
        Next x
        ActiveCell.Offset(0, 1).Select  'select the next column
        c = ActiveCell.Column
        c = GetLetterFromNumber(c)      'get the letter of the next column
    Next H
    Application.ScreenUpdating = True
    MsgBox "Finished"
    Range(FirstCell).Select
End Sub

Function GetLetterFromNumber(Number)
    GetLetterFromNumber = Split(Cells(1, Number).Address(True, False), "$")(0)
End Function

When there are not a lot of rows it is pretty fast, but there are a lot of rows it is slow.

I have a file that I ran it on that has columns from A to AD and 61k+ rows, it took more than 30 minutes to finish and I'm hoping to make that much faster.

4
  • 3
    Stop using Select, use With ... End With, declare data types for your variables.
    – Victor K
    Commented Sep 22, 2017 at 14:24
  • 1
    Using count is unnecessary. You already have x counting the rows. Also, why bother converting to column letter? Column numbers work and you are not feeding this information back to the user.
    – Darrell H
    Commented Sep 22, 2017 at 14:32
  • Start your code with Application.Calculation = xlManual and end it with Application.Calculation = xlAutomatic.
    – Pspl
    Commented Sep 22, 2017 at 14:36
  • 1
    Too many calls to Range() You can declare a single range variable and set it once, and then use .Cells(i,j) in a With block to iterate over it. Even better -- find a way to avoid cell by cell iteration (e.g. by using .Find). Commented Sep 22, 2017 at 14:41

4 Answers 4

5

Instead of looking into Every single cell in the worksheet, use Replace function which is far faster :(you may need to edit it customize it to your needs)

Example :

Sub RemoveNullColumn()

  Dim targetSheet As Worksheet
  Set targetSheet = ActiveSheet 'TODO: replace with a stronger object reference

  targetSheet.Cells.Replace What:="NULL", Replacement:="", LookAt:=xlWhole, _
  SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
   ReplaceFormat:=False
End Sub

This will make sure you will preserve the format.

7
  • Would this find just those cells where the only thing in them is "NULL" or will it find every instance of "NULL" even if it is part of a sentence or something else in the cells?
    – Mike
    Commented Sep 22, 2017 at 14:41
  • 2
    The LookAt:=xlWhole will require that the entire cell value is equal to the What:="NULL". If you want to replace within text, then use xlPart in the LookAt.
    – mooseman
    Commented Sep 22, 2017 at 14:43
  • 3
    At least, take a Range parameter and use that instead of implicitly working against the ActiveSheet (through Cells) and thus forcing the caller to Activate a worksheet. As it stands, this procedure encourages callers to use the worst coding practices. Commented Sep 22, 2017 at 14:47
  • 2
    fwiw, replacing "" with vbNullString is slightly faster.
    – user4039065
    Commented Sep 22, 2017 at 14:56
  • 1
    To be fair, OP's macro is probably invoked from the ActiveSheet - I've edited the code accordingly and revoked my downvote. Commented Sep 22, 2017 at 15:13
1

If you want to clear NULL using ActiveCell as reference:

Range(ActiveCell, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Replace What:="NULL", Replacement:="", LookAt:=xlPart, _
    SearchOrder:=xlByRows, MatchCase:=True, SearchFormat:=False, _
    ReplaceFormat:=False
0

Please give this a try...

Sub RemoveNullColumn()
Dim lr As Long, lc As Long
Dim rng As Range, cell As Range, FirstCell As Range

With Application
    .Calculation = xlCalculationManual
    .EnableEvents = False
    .ScreenUpdating = False
End With

lr = Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
lc = Cells.Find("*", SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column

Set FirstCell = ActiveCell
Set rng = Range(Cells(1, FirstCell.Column), Cells(lr, lc))

For Each cell In rng
    If cell.Value = "NULL" Then
        cell.Clear
    End If
Next cell

With Application
    .Calculation = xlCalculationAutomatic
    .EnableEvents = True
    .ScreenUpdating = True
End With
MsgBox "Finished"
End Sub
0

Use a .Find/.FindNext to collect all of the matching cells into a Union then clear the contents of the Union'ed cells.

Option Explicit

Sub noNULLs()
    Dim firstAddress As String, c As Range, rALL As Range
    With ActiveSheet.Cells    'This should be named worksheet like Worksheets("sheet1")
        Set c = .Find("NULL", MatchCase:=True, _
                      LookIn:=xlValues, LookAt:=xlWhole)
        If Not c Is Nothing Then
            Set rALL = c
            firstAddress = c.Address
            Do
                Set rALL = Union(rALL, c)
                Set c = .FindNext(c)
            Loop While c.Address <> firstAddress
            rALL.Clear
        End If
    End With
End Sub
6
  • If you only wanted to clear the values and not strip any formatting then change .Clear to .ClearContents.
    – user4039065
    Commented Sep 22, 2017 at 14:40
  • I think your loop condition should be like this: Loop While Not c Is Nothing And c.Address <> firstAddress Commented Sep 22, 2017 at 14:40
  • 1
    Makes sense. I tested your code and it errors out at the line With ActiveSheet. It should be With ActiveSheet.Cells and also why did you use the line Worksheets(4).Range(c.Address).Activate? Commented Sep 22, 2017 at 14:46
  • 2
    @sktneer - Thanks for catching those. It was a rewrite of an older answer of mine and I neglected to run through it.
    – user4039065
    Commented Sep 22, 2017 at 14:48
  • 1
    I tested both this and the excepted answer for speed b/c im a nerd & was bored - the accepted answer was faster. However as Mat'sMug pointed out, it has some bad coding practices in it. The speed difference was minimal anyway.
    – Doug Coats
    Commented Sep 22, 2017 at 14:51

Not the answer you're looking for? Browse other questions tagged or ask your own question.