VBA code to delete extra rows in sheet with thousands of rows slowed significantly - excel

We have an Excel macro that cleans up a spreadsheet containing thousands of rows. It starts from the bottom of the sheet and deletes rows that meet certain criteria.
I am told that this macro used to take a few minutes to run, and now it takes an hour. It used to zip up the spreadsheet, and now it takes about a second per row, which obviously adds up.
We recently upgraded from Excel 2007 to Excel 2016, so I am not sure if that is the cause. I have tried the macro on multiple computers and it is slow on all of them, so I don't think it's a faulty install issue. It may just be that the code is written inefficiently, or the spreadsheets have gotten larger. Not sure what else would cause this change.
Here is the code:
Sub DeleteExtraRows()
Dim RowCount As Integer
Dim i As Integer
RowCount = ActiveSheet.Cells(Rows.count, "B").End(xlUp).row
'Delete the Rows
For i = RowCount To 2 Step -1
Range("A" & i).Select
If (Range("A" & i).Style = "Neutral" And Range("AC" & i) = False) Or (Range("U" & i) = 1 And Range("V" & i) = 0 And Range("AC" & i) = False) Then
Rows(i).Delete
End If
Application.StatusBar = RowCount - i & " of " & RowCount & " Records Processed"
Next i
'Delete all the checkboxes
ActiveSheet.CheckBoxes.Delete
Range("A:A").Delete
Application.StatusBar = False
'Move to the top
Range("A2").Select
End Sub

Delete Thousands of Rows
I recently encountered a code that used the status bar in a similar way and slowed down the code dozens of times. Don't do that if it's not necessary.
Not tested.
Option Explicit
Sub DeleteExtraRows()
With ActiveSheet
'Delete the rows
Dim RowCount As Long: RowCount = .Cells(.Rows.Count, "B").End(xlUp).Row
Dim drg As Range
Dim i As Long
For i = 2 To RowCount
If (.Range("A" & i).Style = "Neutral" And .Range("AC" & i) = False) _
Or (.Range("U" & i) = 1 And .Range("V" & i) = 0 _
And .Range("AC" & i) = False) Then
If drg Is Nothing Then
Set drg = .Rows(i)
Else
Set drg = Union(drg, .Rows(i))
End If
End If
Next i
If Not drg Is Nothing Then
drg.Delete
End If
'Delete all the checkboxes
.CheckBoxes.Delete
.Range("A:A").Delete
'Move to the top
.Range("A2").Select
End With
End Sub

Related

Using a cell in a loop to define a range in vba. I want to basically delete the row of that cell and the next 3 ones

I'm basically writing a clean up program to make it more straight forward to access data. Anywho, I ran into possibly a nomenclature error. I want to use the "current" cell in a "for" loop to delete that row and the next 3 rows. Code looks something like this:
For Each SingleCell In SingleSheet1.Range("a1:a40")
If SingleCell.Value = "S" Or SingleCell.Value = "B" Then
Range(SingleCell.Range, SingleCell.Range.Offset(4, 0)).EntireRow.Delete Shift:=xlUp
Else
End If
Next
I tried to define the range to delete as specified in the code but it gave me a runtime error
Delete backwards looping trough row number:
Sub EXAMPLE_1()
Dim i As Long
For i = 40 To 1 Step 1
If Range("A" & i).Value = "S" Or Range("A" & i).Value = "B" Then Range("A" & i & ":A" & i + 3).EntireRow.Delete Shift:=xlUp
Next i
End Sub
Sub EXAMPLE_2()
Dim i As Long
Dim LR As Long 'in case last row is not always number 40, adapt it dinamically
LR = Range("A" & Rows.Count).End(xlUp).Row
For i = LR To 1 Step 1
If Range("A" & i).Value = "S" Or Range("A" & i).Value = "B" Then Range("A" & i & ":A" & i + 3).EntireRow.Delete Shift:=xlUp
Next i
End Sub
Your code looses the reference for the deleted rows and you should iterate backwards, if you like iteration between cells (which is slow), but a better/faster solution will be to build a Union range and delete all rows at the code end, at once:
Sub testDeleteOffset()
Dim sh As Worksheet, Urng As Range, i As Long
Set sh = ActiveSheet
For i = 1 To 40
If sh.Range("A" & i).Value = "S" Or sh.Range("A" & i).Value = "B" Then
addToRange Urng, sh.Range("A" & i, "A" & i + 3)
i = i + 4
End If
Next i
If Not Urng Is Nothing Then Urng.EntireRow.Delete xlUp
End Sub
If the involved range is huge, a better solution will be to place some markers for the necessary rows (after last existing column), sort on that marker column and delete the (consecutive marked) rows. Another column with the initial order would be necessary to re-sort according to it at the end... The idea is that building a Union range having more than 1000 areas may become slow.

Remove blank rows when print

error im getting this error when using your code
Dim answer As Integer
answer = MsgBox("Äðóêóâàòè?", vbYesNo + vbQuestion, "Äðóê")
If answer = vbYes Then
ActiveSheet.PageSetup.PrintArea = "A1:N27"
ActiveWindow.SelectedSheets.PrintOut
Else
'End
End If
End Sub
need the macro to print areas that are field within range A1:N27 and delete blank can someone solve it?
Due to my fault there where three problems that FaneDuru has found with my workbook that his code didn't worked with my workbook
The rows to be hide/deleted are not empty. They contains formulas...
The result of formula on column D:D is "".
The worksheet in discussion is protected, but without a password
Try the next code, please. It will hide the rows being empty on the range B:L, print and then un-hide them. The updated code is done according to your last specifications (there are formulas in the 'empty' rows, in column D:D the formula result is "" and the worksheet is protected, but without a password):
Sub testRemoveRowsPrintAreaSet()
Dim sh As Worksheet, lastRow As Long, rngDel As Range, i As Long
Set sh = ActiveSheet
lastRow = sh.Range("C" & Rows.Count).End(xlUp).Row
For i = 9 To lastRow
Debug.Print WorksheetFunction.CountBlank(sh.Range("B" & i & ":L" & i))
If WorksheetFunction.CountBlank(sh.Range("B" & i & ":L" & i)) = 10 Then
If rngDel Is Nothing Then
Set rngDel = sh.Range("M" & i)
Else
Set rngDel = Union(rngDel, sh.Range("M" & i))
End If
End If
Next i
If Not rngDel Is Nothing Then
sh.Unprotect
rngDel.EntireRow.Hidden = True
End If
sh.PageSetup.PrintArea = "A1:N" & lastRow
ActiveWindow.SelectedSheets.PrintOut
rngDel.EntireRow.Hidden = False
sh.Protect
End Sub
Please, confirm that it work as you need.

Delete cells depend on thier values works fine but skipped the half

I need to delete 2 or more (variable depending on work) cells in the same row starting from row 2 if the 2 cells are = ""
I used this code and it's already working fine except 1 problem
Sub Macro3()
Dim s As Integer
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet2")
For s = 2 To 22
If ws.Range("G" & (s)) = "" And ws.Range("H" & (s)) = "" Then
Union(ws.Range("G" & s), ws.Range("H" & s)).Select
Selection.Delete Shift:=xlUp
End If
Next s
End Sub
the problem is if I have for example from G2:H4 (2rows or more achieve the if condition) it's only delete half of them,
if 5 rows delete 3 only...etc
so I think the loop doesn't operate on the current cell (just guessing)
Attach screens is before and after running the code for more clarification
before
after
Sub Macro3()
Dim s As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet2")
For s = 22 To 2 Step -1
If ws.Range("G" & s).Value = "" And ws.Range("H" & s).Value = "" Then
ws.Rows(s).Delete Shift:=xlUp
'or:
'ws.Range("G" & s & ":H" & s).Delete Shift:=xlUp
End if
Next s
End Sub
After running the code:

Copy row of data based on criteria AND "label" that copied data in last column

I have working code that checks for a criteria in each row, and if met, copies that whole row of data over to a different workbook. But! I need to be able to add text to the last column of the copied data (Column S) that essentially labels what criteria was met that made the code copy it over because I will soon be expanding to check for multiple different criteria.
So for every row that meets the criteria and gets copied, I want to add "Criteria1" next to it in column S in the new workbook (it will always be column S that will be the first available column).
I have mangled this code together through inheritance and all of your help, so I don't really even know where to begin.
Private Sub Workbook_AfterSave(ByVal Success As Boolean)
Dim CoderBook As Workbook
Dim Referrals As Worksheet
Dim Review As Workbook
Dim Crit As Worksheet
Dim LastRow As Long
Dim NextRow As Long
Dim i As Long
Set CoderBook = Workbooks.Open("Coder Referrals.xlsx")
Set Referrals = CoderBook.Sheets("Sheet1")
Set Review = ThisWorkbook
Set Crit = Review.Sheets("Criteria")
'Search code
LastRow = Crit.Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Application.ScreenUpdating = False
'Loop search code
For i = 2 To LastRow
'Specialized Criteria1 Check
If Crit.Range("F" & i) <> Crit.Range("G" & i) Or _
Crit.Range("I" & i) <> Crit.Range("J" & i) Then
'If meets Criteria1 check, then copy appropriate rows to CoderBook Referrals sheet
Referrals.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).EntireRow.Value = Crit.Rows(i).Value
End If
Next i
'End loop code
CoderBook.Close SaveChanges:=True
Application.ScreenUpdating = True
End Sub
Split the or into two statements:
For i = 2 To LastRow
j = Referrals.Cells(Rows.Count, 1).End(xlUp).row + 1
'Specialized Criteria1 Check
If Crit.Range("F" & i) <> Crit.Range("G" & i) Then
'If meets Criteria1 check, then copy appropriate rows to CoderBook Referrals sheet
Referrals.Rows(j).EntireRow.Value = Crit.Rows(i).Value
Referrals.Range("S" & j).Value = "Criteria1"
End If
If Crit.Range("I" & i) <> Crit.Range("J" & i) Then
Referrals.Rows(j).EntireRow.Value = Crit.Rows(i).Value
if Referrals.Range("S" & j).value = vbNullString then
Referrals.Range("S" & j).Value = "Criteria2"
Else
Referrals.Range("S" & j).Value = Referrals.Range("S" & j).Value & ", " & "Criteria2"
End if
Next i

Merge empty cells with previous value

I have an Excel file with around 100,000 records. I have 6+ columns, the first five of which are:
Required Format:
So far I have :
Sub Main()
Dim i As Long
Dim j As Long
Dim sameRows As Boolean
sameRows = True
For i = 1 To Range("A" & Rows.Count).End(xlUp).Row
For j = 1 To 4
If StrComp(Cells(i, j), Cells(i + 1, j), vbTextCompare) Then
sameRows = False
End If
Next j
If sameRows Then
Range(Cells(i, 4), Cells(i + 1, 4)).merge
End If
sameRows = True
Next i
End Sub
I am able to get below by running the macro by changing value in Range from 4 to 1/2/3/4 and running macro four times.
Please help me get the data in required format. I still need to merge the empty fields with the previous non empty field.
Pratik, listen carefully to Jeeped. Working with large data in Excel isn't ideal, and working with raw data in merged cells is staring into the abyss - it's a dark, dark place where Range referencing and things like Offset functions will show you a dimension of despair you never knew existed.
If you have this data in another format, say XML, that you've imported into Excel then use VBA to read the data, query it, etc. in its original format. If it exists in a database, then, again, use VBA to access that database and manipulate the recordsets as you wish. If this is your only source of data, then why not write it into an XML document or into VBA's own data storage options (like Collection or arrays).
If you have to use Excel then don't confuse raw data with data display. Yes, the merged cells might be easier to read for the human eye, but I'd just pose the question: is that your primary objective in conducting the merge?
If you must take that leap into the abyss - and you can see that at least two of us would advise against - then at least speed things up by reading from an array and merging rows at a time:
Sub OpenDoorsToHades()
Dim dataSheet As Worksheet
Dim v As Variant
Dim mergeCells As Range
Dim mergeAreas As Range
Dim i As Long
Dim blankStart As Long
Dim blankEnd As Long
Dim doMerge As Boolean
Dim c As Integer
Set dataSheet = ThisWorkbook.Worksheets("data") 'rename to your sheet
'Read values into array of variants
With dataSheet
v = .Range("A1", .Cells(.Rows.Count, "A").End(xlUp)).Value2
End With
'Check for blanks
For i = 1 To UBound(v, 1)
If IsEmpty(v(i, 1)) Then
If Not doMerge Then
blankStart = i - 1
doMerge = True
End If
Else
If doMerge Then
blankEnd = i - 1
For c = 1 To 4
With dataSheet
Set mergeCells = .Range( _
.Cells(blankStart, c), _
.Cells(blankEnd, c))
If mergeAreas Is Nothing Then
Set mergeAreas = mergeCells
Else
Set mergeAreas = .Range(mergeAreas.Address & _
"," & mergeCells.Address)
End If
End With
Next
mergeAreas.Merge
Set mergeAreas = Nothing
doMerge = False
End If
End If
Next
'Format the sheet
dataSheet.Cells.VerticalAlignment = xlCenter
Application.ScreenUpdating = True
End Sub
How about just populating the empty cells with the values above, so the values on the far right are associated with the same values that would've been in the merged cell. For example, if 19 is in cell A2, you can recreate the table starting in G2 with =IF(A2<>"",A2,G1), and this way all empty cells will be populated with the value above, pairing the values at the far right with the exact same values.
I tackled the same problem myself this week. Ambie's solution seemed overly complex, so I wrote something pretty simple to merge rows:
Sub MergeRows()
Sheets("Sheet1").Select
Dim lngStart As Long
Dim lngEnd As Long
Dim myRow As Long
'Disable popup alerts that appear when merging rows like this
Application.DisplayAlerts = False
lngStart = 2
lngEnd = 2
For myRow = 2 To Range("A" & Rows.Count).End(xlUp).Row 'last row
If Range("A" & (myRow + 1)).value = "" Then
'include row below in next merge
lngEnd = myRow + 1
Else
'merge if 2+ rows are included
If lngEnd - lngStart > 0 Then
Range("A" & lngStart & ":A" & lngEnd).Merge
Range("B" & lngStart & ":B" & lngEnd).Merge
Range("C" & lngStart & ":C" & lngEnd).Merge
Range("D" & lngStart & ":D" & lngEnd).Merge
End If
'reset included rows
lngStart = myRow + 1
lngEnd = myRow + 1
End If
Next myRow
Application.DisplayAlerts = True
End Sub

Resources