Speeding up deleting of columns in Excel - excel

I'm using this formula to delete empty columns in my sheet. But it takes around 15-20min to delete them. Can I somehow speed up this proces? It's weird that it takes so long and I'm only working with 100 columns.
For j = 1 To 5
For i = 40 To 146
If Sheet8.cells(4, i) = "" Then
Columns(i).EntireColumn.Delete
End If
Next i
Next j

If you have real bank cells use #Patrick Honorez answer using SpecialCells(xlCellTypeBlanks) which is faster.
But if you have no real blank cells (eg formulas that show as "") then you can use this:
Dim DeleteRange As Range
With sheet8
Dim i As Long
For i = 40 To 146
If .Cells(4, i).Value = vbNullString Then 'vbNullString = ""
If DeleteRange Is Nothing Then
Set DeleteRange = .Columns(i)
Else
Set DeleteRange = Union(DeleteRange, .Columns(i))
End If
End If
Next i
End With
If Not DeleteRange Is Nothing Then 'check if there is something to delete
DeleteRange.EntireColumn.Delete
End If
It collects all columns that you want to delete in DeleteRange and then deletes them at once. This is faster than deleting each column on its own, because each delete action needs its time (and here we have only one delete action).
Note that we don't need to loop backwards here because we only collect the columns within the loop but delete action comes after the loop, so it doesn't affect the loop counting at all.

Try:
For i = 146 To 40 step -1
If Sheet8.cells(4, i) = "" Then
Columns(i).EntireColumn.Delete
End If
Next i

Try adjusting this for your needs. I think nothing can go faster :-)
Sub test()
Range("C3:j17").SpecialCells(xlCellTypeBlanks).EntireColumn.Delete
End Sub

I would suggest to do it like that.
Option Explicit
Private Sub TurnOffFunctionality()
Application.Calculation = xlCalculationManual
Application.DisplayStatusBar = False
Application.EnableEvents = False
Application.ScreenUpdating = False
End Sub
Private Sub TurnOnFunctionality()
Application.Calculation = xlCalculationAutomatic
Application.DisplayStatusBar = True
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Sub TestIt()
Dim i As Long
TurnOffFunctionality
For i = 146 To 40 Step -1
If Sheet8.Cells(4, i) = "" Then
Columns(i).EntireColumn.Delete
End If
Next i
TurnOnFunctionality
End Sub
Turning off especially re-calculation will make sure that a re-caculation is not triggered with every deletion of a single column in the loop. This could probably be the cause of the long run time.

Related

Why does the Range method Select work the first time but not the second

Sub History1()
'
' History Macro
'
'
Dim sDate As String
Application.ScreenUpdating = False
sDate = Sheets("Summary").Range("P1").Value
If Not WorksheetExists(sDate) Then
Sheets.Add.Name = sDate
End If
Sheets(sDate).Visible = True
Sheets(sDate).Cells.UnMerge
Sheets("Summary").Range("A1:Z100").Select
Selection.Copy
Sheets(sDate).Range("A1:Z100").Select
Selection.Paste
Sheets(sDate).Visible = False
Sheets("Summary").Cells(3, 1).Select
Application.ScreenUpdating = True
' MsgBox ("Done")
End Sub
The value in sDate is the string 05_14_21 and works fine when used earlier in the sub. The first instance of Range.Select operation works fine, at least I think it does. No error generated. The second one says "Select method of Range class failed". The worksheet "05_14_21" is visible at the time of the 2nd operation. I am at a loss. Any insights will be appreciated.Th
You cant use .Select on a range of a sheet that isn't active. It will give you Run-Time Error 1004. The fix would be to first select that sheet, then select the range. But honestly, using .Select is unnecessary and will make the screen jitter around while the macro is running. .Select also slows down your macro, which becomes very noticeable when you start using loops in your code.
Instead I would suggest directly referencing your ranges instead of using .Select like so:
Sub History1()
Dim sDate As String
Application.ScreenUpdating = False
sDate = Sheets("Summary").Range("P1").Value
If Not WorksheetExists(sDate) Then
Sheets.Add.Name = sDate
End If
Sheets(sDate).Visible = True
Sheets(sDate).Cells.UnMerge
Sheets("Summary").Range("A1:Z100").Copy Destination:= Sheets(sDate).Range("A1:Z100")
Sheets(sDate).Visible = False
Application.ScreenUpdating = True
' MsgBox ("Done")
End Sub
I am not sure why you need to select Cells(3,1), but in order to do that you need to activate the correct sheet. As pointed out in another answer, using select is not advisable. This also results in a more concise way to copy and paste.
Dim sDate As String
Application.ScreenUpdating = False
sDate = Sheets("Summary").Range("A1").Value
If WorksheetExists(sDate) = False Then
Sheets.Add.Name = sDate
End If
Sheets(sDate).Visible = True
Sheets(sDate).Cells.UnMerge
Sheets("Summary").Range("A1:Z100").Copy _
Destination:=Sheets(sDate).Range("A1:Z100")
Sheets(sDate).Visible = False
Sheets("summary").Activate
Sheets("Summary").Cells(3, 1).Select
Application.ScreenUpdating = True

Pause VBA Script While Links Update

This is my second post about this macro. Although the first post received a few responses, none of the responses solved the problem (thank you for responding though).
Scenario:
I have about 20 sub-spreadsheets with links to external sources. The number of links per spreadsheet varies from about 500 to 10,000. A master spreadsheet calls macros to open each sub-spreadsheet in turn and update the links.
Each sub-spreadsheet has a dashboard that tells me how many links remain to be updated. This is done by counting the number of “N/A” values in each tab, then summing these counts in cell A20. As the links are updated, the value in A20 counts down to zero.
Sub Sub01()
Dim NAtotal As Integer
Set ActiveWKB = Workbooks.Open("Sub01.xlsm")
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
Application.CalculateFull
ActiveWorkbook.UpdateLink Name:=ActiveWorkbook.LinkSources
NAtotal = Worksheets("Dashboard").Cells(20, "C").Value
MsgBox (NAtotal) 'Tells me how many cells remain to be updated – starts off at 4450.
NAtotal = 100 'Debugging effort to let me know that NAtotal does adjust.
MsgBox (NAtotal)
Do Until NAtotal = 0
Application.ScreenUpdating = True
MsgBox (NAtotal) 'Another debugging effort to monitor NAtotal. Starts at 100, then jumps to (and remains at) 4450 on the second loop and all subsequent loops.
NAtotal = Worksheets("Dashboard").Cells(20, "C").Value 'Resets NAtotal to the value in C20. This never changes, but remains at 4450.
DoEvents
Loop
Application.Calculation = xlManual
MsgBox ("Done")
Sheets("Dashboard").Activate
Range("B1").Select
ActiveWorkbook.Save
ActiveWindow.Close
End Sub`
The macro should continue to loop until cell A20 hits zero, and then stop.
Cell A20 does count down, but variable NAtotal remains at its initial value.
Any guidance/recommendations are appreciated.
Hi the code below worked for me. Try use the same method instead of using a loop. The schedule will trigger every second until the NATotal = 0 logically anyway. Just update the code to fit your references.
Public firstOpen As Boolean
Sub testForm()
Dim cellCount As Integer
Dim s1 As Sheet1
Set s1 = Sheet1
Dim cellCol As Integer
Dim activeWbk As Workbook
Dim ws As Worksheet
If firstOpen = False Then
firstOpen = True
Set activeWbk = Workbooks.Open("C:\Example\Link2.xlsm")
Set ws = activeWbk.Sheets("Sheet1")
Application.Calculation = xlCalculationAutomatic
Application.CalculateFull
activeWbk.UpdateLink Name:=ActiveWorkbook.LinkSources
CreateNewSchedule
Exit Sub
Else
Set activeWbk = Workbooks("Link2.xlsm")
Set ws = activeWbk.Worksheets("Sheet1")
End If
cellCount = ws.Range("N2").Value
If cellCount = 0 Then
MsgBox ("Done...")
Application.Calculation = xlCalculationManual
firstOpen = false
Else
Debug.Print cellCount
CreateNewSchedule
End If
'Application.Calculation = xlCalculationManual
End Sub
Sub CreateNewSchedule()
Application.OnTime Now + TimeValue("00:00:01"), Procedure:="testForm", Schedule:=True
End Sub

Looping over checkboxes with VBA in Excel very slow

I have an Excel Sheet with about 4500 checkboxes (I know, it sounds stupid, but it is for a customer, please do not ask...).
Just wrote the VBA Sub below to uncheck all the boxes together. So far it works, but it is terribly slow, it takes more than 5 minutes until all boces are unchecked and while the Sub is running, the whole Excel Applikation grays out freezes. I know, 4500 Checkboxes is quiet a lot, but I wonder that it is really enough to bring Excel in such a trouble....Has anyone an idea?
Best
Michael
Sub DeselectAll()
Application.EnableCancelKey = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim wksA As Worksheet
Dim intRow As Integer
Set wksA = Worksheets("Companies")
For intRow = 1 To 4513
wksA.CheckBoxes("Checkbox_" & intRow).Value = False
Next
End Sub
Without selection:
Sub DeselectAll()
With Worksheets("Companies").CheckBoxes
.Value = xlOff
End With
End Sub
Just don't loop.
This is a good example of when Selection can help:
To set all checkboxes:
Sub dural()
ActiveSheet.CheckBoxes.Select
Selection.Value = xlOn
End Sub
To uncheck all checkboxes:
Sub dural2()
ActiveSheet.CheckBoxes.Select
Selection.Value = xlOf
End Sub
( tested on Forms-type checkboxes )
The best answer I thumbs up for is #EvR solution. I am not trying to answer but offering an idea of a workaround.
I checked the time by adding 4000 ComboBox in blank sheet in a blank workbook with a simple 3 line loop (omg I forgot to off screen updating and calculations etc). It took around 10 minutes in my old laptop. I don’t have courage to repeat the ordeal again.
When I tried to use your piece of code with looping it is taking 3-4 seconds only and with #EvR’s solution without loop and selection is taking 1-2 seconds. These times are actual time taken with Debug.Print or writing to some cells. Actual drama unfolds after screen updates, calculations, events are enabled with the sheet active. It become highly unstable and any careless click etc cause excel to ‘not responding’ state for 2-5 mintues.
Though Customer and Boss are always right. Once in my life I succeeded to persuade someone in a similar approach of hundreds of buttons on a worksheet to something virtual. My Idea is to create virtual checkbox in the sheet. Proper cell sizing and border with validation of the cells to `=ChrW(&H2714)’ and ignore blank and a simple code like below can make it a pass-through type of work-around.
Public Prvsel As Range
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim isect, Cl As Range
Set isect = Application.Intersect(Target, Range("C1:C4000"))
If isect Is Nothing Then
Set Prvsel = Nothing 'Release multiple selection
Exit Sub
End If
If isect.Cells.Count > 1 Then
Set Prvsel = isect 'storing multiple selection for next click event
Else
If Target.Value = ChrW(&H2714) Then
Target.Value = ""
Else
Target.Value = ChrW(&H2714)
End If
If Not Prvsel Is Nothing Then
For Each Cl In Prvsel.Cells
Cl.Value = Target.Value
Next Cl
End If
End If
End Sub
Elaborating on #Ahmed AU solution.
Select/Deselect signal/ multiple virtual checkboxs
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim isect As Range
Dim Cl As Range
Dim Prvsel As Range
Set isect = Application.Intersect(Target, Range("C1:C4000"))
If isect Is Nothing Then
Set Prvsel = Nothing 'Release multiple selection
Exit Sub
End If
' Use WINGDING font Chr (254) for checked
' Use WINGDING font Chr (111) for uncheck
If isect.Cells.Count >= 1 Then
Set Prvsel = isect
For Each Cl In Prvsel.Cells
If Cl.Value = Chr(111) Then
Cl.Value = Chr(254)
Else
Cl.Value = Chr(111)
End If
Next Cl
End If
'Go to offset cell selection
Selection.Offset(0, 1).Select
End Sub

Activate macro automatically without having to click on target cell

I have a macro that hides certain rows when the values in a cell change. However this macro is not running unless you enter the target cell and click on it. I have tried several alternatives but none work for me.
Sheet
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("$b$156").Value = 1 Then Call oculta_4
If Range("$b$156").Value = 2 Then Call oculta_5
If Range("$b$156").Value = 3 Then Call oculta_6
If Range("$b$156").Value = 4 Then Call oculta_7
End Sub
Macro
Sub oculta_4()
Rows("158:176").EntireRow.Hidden = False
Range("$c$158").Select
For Each celda In Range("$c$158:$c$176")
If celda.Value = 0 Then
ActiveCell.EntireRow.Hidden = True
End If
ActiveCell.Offset(1).Select
Next
End Sub
As others have said, to respond to a value changed by a Formula, you need to use Worksheet_Calculate.
As Worksheet_Calculate does not have a Target property, you need to create your own detection of certain cells changing. Use a Static variable to track last value.
You should also declare all your other variables too.
Repeatedly referencing the same cell is slow and makes code more difficult to update. Put it in a variable once, and access that
Select Case avoids the need to use many If's
Don't use Call, it's unnecessary and obsolete.
Adding Application.ScreenUpdating = False will make your code snappy, without flicker
Writing the hidden state of a row takes a lot longer than reading it. So only write it if you need to.
Something like this (put all this code in the code-behind your sheet (that's Hoja1, right?)
Private Sub Worksheet_Calculate()
Static LastValue As Variant
Dim rng As Range
Set rng = Me.Range("B156")
If rng.Value2 <> LastValue Then
LastValue = rng.Value2
Select Case LastValue
Case 1: oculta_4
Case 2: oculta_5
Case 3: oculta_6
Case 4: oculta_7
End Select
End If
End Sub
Sub oculta_4()
Dim celda As Range
Application.ScreenUpdating = False
For Each celda In Me.Range("C158:C176")
With celda.EntireRow
If celda.Value = 0 Then
If Not .Hidden Then .Hidden = True
Else
If .Hidden Then .Hidden = False
End If
End With
Next
Application.ScreenUpdating = True
End Sub

VBA - Runtime error with END IF

I am relatively new to VBA. And when I was working on a worksheet I created a code that automatically hides/unhides rows based on a condition in a column row (0 unhide/1 hide). This relatively easy macro worked well until I added a different sheet. As there are no macros in this sheet I dont think it is related. But now everytime it gives a runtime error on the END IF function and I don't know how to solve it. There is probably a simple solution, but I cannot find it.
Here is the code:
Private Sub Worksheet_Calculate()
Application.ScreenUpdating = False
Dim LastRow As Long, c As Range
Application.EnableEvents = False
LastRow = Cells(Cells.Rows.Count, "BA").End(xlUp).Row
On Error Resume Next
For Each c In Range("BA34:BA56,BA73:BA74,BA76:BA107")
If c.Value = 1 Then
c.EntireRow.Hidden = True
ElseIf c.Value = 0 Then
c.EntireRow.Hidden = False
End If
Next
On Error GoTo 0
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Remove or comment out the On Error Resume Next if you want more complete debugging information. It should not be necessary in an event macro that runs as often as Worksheet_Calculate.
Apply a parent worksheet reference to the range object. The worksheet may have volatile formulas that are triggering a calculation event due to changes on the other worksheet.
I've added a wide condition so that the code will only run if the parent worksheet holds the ActiveSheet property. The worksheet may have volatile¹ formulas that are triggering a calculation event due to changes on the other worksheet (or even another workbook).
LastRow does nothing after its assignment (which is problematic) so I removed it.
I simplified the Range.Hidden property to a single line.
Private Sub Worksheet_Calculate()
If Me.Name = Activesheet.Name Then
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim c As Range
For Each c In Me.Range("BA34:BA56,BA73:BA74,BA76:BA107")
c.EntireRow.Hidden = CBool(c.Value2)
Next c
Application.EnableEvents = True
Application.ScreenUpdating = True
End If
End Sub
¹ Volatile functions recalculate whenever anything in the entire workbook changes, not just when something that affects their outcome changes. Examples of volatile functions are INDIRECT, OFFSET, TODAY, NOW, RAND and RANDBETWEEN. Some sub-functions of the CELL and INFO worksheet functions will make them volatile as well.
Just to finish the thread. I solved it based on a combination of the code you wrote and mine, as the 0 should be the trigger to unhide as well. I guess the error line caused the issue.
Private Sub Worksheet_Calculate()
Application.ScreenUpdating = False
Dim LastRow As Long, c As Range
Application.EnableEvents = False
LastRow = Cells(Cells.Rows.Count, "BA").End(xlUp).Row
On Error Resume Next
For Each c In Range("BA34:BA56,BA73:BA74,BA76:BA107")
If c.Value = 1 Then
c.EntireRow.Hidden = True
ElseIf c.Value = 0 Then
c.EntireRow.Hidden = False
End If
Next
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub

Resources