VBA FOR loop runs for two loops - vba

I'm trying to copy and paste a list of names from one worksheet to a specific cells in another sheets, depends on the row # in the "List" sheet is even or odd.
However, the FOR loop only runs for two loops : ie. ONE loop for name in row # even and ONE loop for name in row # odd.
Can anyone tell me where is my mistake?
I took away the IF function, and everything works well. But i need the IF function to determine Odd/Even row number.
My current code as below:
Sub Update_Print()
Dim i As Integer
For i = 7 To 1000
Sheets("List").Select
If i Mod 2 > 0 Then
Cells(i, 1).Select
Selection.Copy
Sheets("Receipts").Select
Cells(i + 30, 4).Select
ActiveSheet.Paste
Else
Cells(i, 1).Select
Selection.Copy
Sheets("Receipts").Select
Cells(i + 30, 10).Select
ActiveSheet.Paste
ActiveSheet.Print
Exit For
End If
Next i
End Sub

the for loop and if..else loop are probably misplaced.
May I suggest the following instead, which also tries to avoid the use of copy-pasting which usually slows the execution time.
Sub Update_Print()
Dim i As Integer
Dim sht1, sht2 As Worksheet
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Set sht1 = ActiveWorkbook.Sheets("List")
Set sht2 = ActiveWorkbook.Sheets("Receipts")
sht1.Activate
For i = 7 To 1000
If i Mod 2 > 0 Then
sht1.Cells(i, 1) = sht2.Cells(i + 30, 4)
Else
sht1.Cells(i, 1) = sht2.Cells(i + 30, 10)
End If
Next i
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
sht1.PrintOut
End Sub
Are you trying to print the sheet after the macro runs? I've noticed the placement of your ActiveSheet.Print might be in a rather dangerous position, as it would be printing your activesheet about 1000 times! I've allocated it outside of the loop instead after the complete execution of the For Loop.
This method would be faster for copying values, but it doesn't bring across any formatting.

Related

How to improve run time of a loop for a big database?

For every ID on my database i have to sum all the concepts linked to them. For example ID 2354 has 3 concepts, each one on a different row, i have to sum the amount of the 3 concepts, paste that sum on the cell where the amount of the first of this concepts originally was, and then eliminate the entire row of the second and third concepts.
My macro already do this, but the running time is very high, and my database is huge, around 100,000 rows, i need a way to improve the running time. Here is the code i have:
Sub Macro1()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Dim t As Long
Dim a As Integer
Dim i As Integer
lastt = Cells(Rows.Count, "A").End(xlUp).Row vacĂ­a
For i = 1 To 30
If Cells(1, i).Text = "Report Legacy Key" Then
For t = 2 To lastt
For a = 1 To 40
primera = Cells(t, i).Value
ultima = Cells(t + a, i).Value
repetidas = Range(Cells(t, i + 1), Cells(t + a, i + 1))
If primera = ultima Then
c = Application.WorksheetFunction.Sum(repetidas)
Cells(t, i + 1).Activate
ActiveCell.Value = c
Range(Cells(t + 1, "A"), Cells(t + a, "AB")).Select
Selection.Delete Shift:=xlUp
End If
Next a
If IsEmpty(Cells(t + 1, "A").Value) = True Then
Exit For
End If
Next t
End If
Next i
Application.ScreenUpdating = True
Range("M1").Select
Application.CutCopyMode = False
End Sub
This code allows to do the process for 1000 rows in about 15 seconds, which is very slow, considering it has to be done for around 100,000 rows.
You can do several things to improve execution time:
Instead of working with Cells, use arrays or, even better, Scripting.Dictionary. This option alone will speed up quite a lot.
Sometimes we tend to process data directly on the worksheet, but many basic operations can be done directly in SQL, like sums.
Try avoid redundand operations, like selecting a Range and then using the Selection. Do your operation directly on the Range.
For example you can change this:
Range(Cells(t + 1, "A"), Cells(t + a, "AB")).Select
Selection.Delete Shift:=xlUp
To this:
Range(Cells(t + 1, "A"), Cells(t + a, "AB")).Delete Shift:=xlUp
Following this suggestions should speed things up quite a lot.
Hope this helps.

Excel VBA: Code To Delete Row IF Blank Cell; Optimization

Essentially, when running the below code within one workbook (1 sheet) it completes within an instant. But when using it in my main workbook (couple of sheets, barely any data) it takes a while to complete. How can I optimize the below code?
Sub DeleteBlankRows()
On Error Resume Next
Sheets("Sheet4").Activate
Columns("D").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End Sub
Try avoiding the use of an entire column, as well as .Activate:
Sub DeleteBlankRows()
' On Error Resume Next
Dim lastRow As Long
With Sheets("Sheet4")
lastRow = .Cells(Rows.Count, 4).End(xlUp).row
.Range(.Cells(1, 4), .Cells(lastRow, 4)).SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End With
End Sub
Edit: Commented out the On Error Resume Next
you could try too to stop the automatic calculation and screen update and at the end reenable all.
try this and test too with the other codes
Sub DeleteBlankRows()
Application.ScreenUpdating = False
Application.Calculation = xlManual
On Error Resume Next
Sheets("Sheet4").Activate
Columns("D").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Application.ScreenUpdating = true
Application.Calculation = xlAutomatic
End Sub
Good Luck
lastRow = .Cells(Rows.Count, 4).End(xlUp).row
I never use this method for figuring out last row. It takes too long... Basically processing every cell starting from the bottom of the worksheet. Instead, I count the number of cells with values. I use that number to run a for loop which tests to see if there is a value in a given cell and counts until all cells with values are accounted for. Code wise, its more complicated... but in my experience executes more quickly.
kount = Application.WorksheetFunction.CountA(krng) 'Count how many used cells there are
kRow = 1
j = 1
Do Until j = kount + 1 'Do until all used cells are acounted for
If Cells(kRow, l).Value = vbNullString Then 'If current cell is empty skip it
Else
j = j + 1 'If the current cell has a value count up
End If
kRow = kRow + 1 'but go on to the next row either way
Loop
Where kRow is the last row with a value

Delete entire rows when cells in multiple columns are blank or 0

I am trying to write a code which basically looks at rows 13-33 and deletes the entire row if the cells in Columns B-M are all Blank AND column A is NOT blank.
The first code which I have written below deletes the entire row only if the cell in Column B is blank but I need all the cells in B-M to be blank in order to delete the entire row.
Sub scheduleA()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Sheets("Schedule A Template").Select
Dim RowstoDelete As Long
x = 33
For RowstoDelete = Cells(x, 2).End(xlUp).Row To 13 Step -1
If (Cells(RowstoDelete, 2).Value = "0") And (Cells(RowstoDelete, 1).Value <> "") Then
Rows(RowstoDelete).Delete Shift:=xlUp
End If
Next RowstoDelete
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
I tried writing it differently as well in the following code but can't achieve the desire result.
Sub DeleteRows()
Dim i As Integer
For i = 33 To 13 Step -1
If WorksheetFunction.CountA(Range("B" & i, "M" & i)) = 0 And WorksheetFunction.CountA(Range("A" & i)) <> "" Then
Rows(i).EntireRow.Delete
End If
Next i
End Sub
Please help!
Your conditions for row deletion are: column A not blank, columns B to M blank. Then something like this should do the trick:
Sub ScheduleA()
On Error GoTo errHandler
Const TOP_ROW As Long = 13
Const BOTTOM_ROW As Long = 33
Dim rowIndex As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With ThisWorkbook.Worksheets("Schedule A Template")
For rowIndex = .Cells(BOTTOM_ROW, "A").End(xlUp).Row To TOP_ROW Step -1
If Not IsEmpty(.Cells(rowIndex, "A").Value2) Then '...column A is not blank.
If Application.WorksheetFunction.CountA(.Range(.Cells(rowIndex, "B"), .Cells(rowIndex, "M"))) = 0 Then '...all cells on row rowIndex from columns B to M are blank.
.Rows(rowIndex).Delete Shift:=xlUp
End If
End If
Next
End With
Cleanup:
On Error Resume Next
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Exit Sub
errHandler:
MsgBox Err.Description, vbExclamation + vbOKOnly, "Error"
Resume Cleanup
End Sub
Note that the .Select is gone; you almost never have to select anything to get the job done. Not relying on the selection will make your code much more robust. In the code above, the With block tells the code within it to refer to the target worksheet whenever an expression starts with a period, such as .Cells.
Also, when turning off ScreenUpdating and Calculation, systematically include error handling to turn them back on. This way, if something goes wrong, your code won't leave Excel in an undesirable state.
Finally, instead of referring to worksheets by their tab's name (as seen from Excel), you can refer to them directly using their CodeName, as seen from the VBA editor, in the Properties window, under the worksheet's (Name) property (press Ctrl+R to show the Project Explorer, click on the worksheet under the Microsoft Excel Objects node, then press F4 to display the Properties window). You can change this value; I'd typically change it to shtScheduleATemplate. Then, the With line could be re-written as:
With shtScheduleATemplate
...which would still work even after you changed the worksheet's name from Excel.
EDIT: in your question's code, you are checking column B when determining at which bottom row index to start the loop. However, by doing so, you may miss some rows that should be deleted. I've changed my answer to check within column A instead:
For rowIndex = .Cells(BOTTOM_ROW, "A").End(xlUp).Row To TOP_ROW Step -1

Partial row copying from one sheet to another and mixed type data cell comparing

I have a working macro, that basically cuts row from base sheet, if values in rows fist cell (column A) matches a value in target sheets cell = B1, and to paste it in target sheets first empty row (checks cells in column A). But as the functionality of my Excel needs to be slightly changed, I need to make some adjustments, but all my attempts have failed so far.
Here is the working code:
Sub RowCopy()
Application.ScreenUpdating = False
Set shtarget = Sheets("TargetSheet")
Set shBase = Sheets("BaseSheet")
For i = shBase.Range("A" & shBase.Rows.Count).End(xlUp).Row To 1 Step -1
If shBase.Cells(i, 1).Value = shtarget.Cells(1, 2).Value Then
shBase.Rows(i).EntireRow.Cut
shtarget.Rows(shtarget.Cells(shtarget.Rows.Count, 1).End(xlUp).Row + 1).EntireRow.Insert Shift:=xlDown
shBase.Rows(i).EntireRow.Delete
End If
Next i
Application.CutCopyMode = False
Set shtarget = Nothing
Set shBase = Nothing
Application.ScreenUpdating = True
End Sub
And here are issues, which I'm dealing with:
Issue nr.1:
The code does not work if cell B1 contains mixed text and number and (dash/comma/space), for example: "white - 32". I've tried to use Variant, but it did not work correctly each time and made data sorting quite slower especially with large data amount.
Here I've tried to compare two cells with StrComp, the code itself didn't show any errors, but also did not do the thing that it should do - which is - copying data to target sheet:
Sub RowCopy()
Application.ScreenUpdating = False
Set shtarget = Sheets("TargetSheet")
Set shBase = Sheets("BaseSheet")
For i = shBase.Range("A" & shBase.Rows.Count).End(xlUp).Row To 1 Step -1
If StrComp(shBase.Cells(i, 1).Value, shtarget.Cells(1, 2).Value) = 0 Then
shBase.Rows(i).EntireRow.Cut
shtarget.Rows(shtarget.Cells(shtarget.Rows.Count, 1).End(xlUp).Row + 1).EntireRow.Insert Shift:=xlDown
shBase.Rows(i).EntireRow.Delete
End If
Next i
Application.CutCopyMode = False
Set shtarget = Nothing
Set shBase = Nothing
Application.ScreenUpdating = True
End Sub
What am I missing?
Is there more efficient way to compare mixedtype data in cells?
Issue nr.2:
With the existing code, copying entire row interferes with data in target sheets right part of the page, as it is shifting rows down.
But, it is necessary to cut/copy certain part of row (for example: from A2:J2) from base sheet and paste only data in target sheets region from A to J, while not messing up other part of the target sheet.
It should act more like stepping 1 row down, not inserting and shifting rows, which is happening with the existing code.
I've tried substituting "EntireRow" with Range(A2:J2), but it only left me with necessary data missing and wrong data copying to my target sheet.
How to define specific Range of a row in code below to paste only data in target sheet, while not inserting new rows (and not messing up other data which is out of the target sheets A:J range)?
Sub RowCopy()
Application.ScreenUpdating = False
Set shtarget = Sheets("TargetSheet")
Set shBase = Sheets("BaseSheet")
For i = shBase.Range("A" & shBase.Rows.Count).End(xlUp).Row To 1 Step -1
If shBase.Cells(i, 1).Value = shtarget.Cells(1, 2).Value Then
shBase.Rows(i).EntireRow.Cut
shtarget.Rows(shtarget.Cells(shtarget.Rows.Count, 1).End(xlUp).Row + 1).EntireRow.Insert Shift:=xlDown
shBase.Rows(i).EntireRow.Delete
End If
Next i
Application.CutCopyMode = False
Set shtarget = Nothing
Set shBase = Nothing
Application.ScreenUpdating = True
End Sub
I don't see any issues with searching for (eg) "white - 32"...
Sub RowCopy()
Dim shtarget As Worksheet, shBase As Worksheet
Dim vGet, cDest As Range, i As Long
Application.ScreenUpdating = False
Set shtarget = Sheets("TargetSheet")
Set shBase = Sheets("BaseSheet")
'get initial paste position
Set cDest = shtarget.Cells(shtarget.Rows.Count, 1).End(xlUp).Offset(1, 0)
'value being searched for
vGet = shtarget.Cells(1, 2).Value
For i = shBase.Range("A" & shBase.Rows.Count).End(xlUp).Row To 1 Step -1
If shBase.Cells(i, 1).Value = vGet Then
shBase.Cells(i, 1).Resize(1, 10).Copy cDest 'copy 10 columns
shBase.Rows(i).EntireRow.Delete
Set cDest = cDest.Offset(1, 0)
End If
Next i
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub

Comparing Ranges and Copying

I have been creating a small project that will allow a user to import and export data from work worksheet to another. I will attach screenshots to try and explain what i am trying to achieve.
I have the import section of my program working without fault and i can import all jobs that are of colour "Red" from my second worksheet. However once the row has been changed to colour "Green" in worksheet 1 it will then be exported back to sheet 2 and in turn will change the once "Red" job to "Green" effecting no other rows in sheet 2.
I have tried to implement the code as best as i could however i keep getting errors when comparing the my unique cell in both ranges.
As of just now when i run the code it will copy over the value 10 times and paste over all data from row "A4" to row "A14"
Worksheet One
Worksheet Two
Sub Button3_Click()
'#Author - Jason Hughes(AlmightyThud)
'#Version - 1.0
'#Date - 0/03/2015
'#Description - To Export all Completed Jobs to the "Daily Work Orders" Spreadsheet
'Once exported it will scan for the unique job number in the list and override the existing values
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = False
Application.EnableEvents = False
'Declare initial variables for this button'
Dim copyComplete As Boolean
copyComplete = False
Dim lR As Long
'----------------------------------'
'#When this code is uncommented it will delete all values in column A#'
Dim jobID As Range
Dim jobID2 As Range
Set jobID = Sheets("Daily Screen Update").Range("A4:A31")
Set jobID2 = Sheets("Daily Work Orders").Range("A4:A10000")
'----------------------------------'
'Activate the sheet you will be looping through'
ThisWorkbook.Sheets("Daily Screen Update").Activate
'Simple loop that will loop through all cells to check if the cell is green'
'If the cell is green then the loop will copy the cell, once copied the loop will check'
'the "Daily Work Orders" Sheet for a job ID with a similar ID and paste over it'
For Each greenjob In Range("A4:A31")
If greenjob.Cells.EntireRow.Interior.Color = RGB(146, 208, 80) Then
greenjob.Cells.EntireRow.Copy
For j = 4 To 31
For i = 4 To 10
If jobID.Cells(j, 1).Value = jobID2.Cells(i, 1).Value Then
Sheets("Daily Work Orders").Range("A" & j).PasteSpecial xlPasteAll
copyComplete = True
End If
Next i
Next j
End If
Next
'Make a check to ensure that the data has been copied
If copyComplete = True Then
MsgBox ("All completed jobs have been have been added to Daily Work Orders")
ElseIf copyComplete = False Then
MsgBox ("Nothing has been added to Daily Work Orders")
End If
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.CutCopyMode = False
End Sub
You have three For loops:
For Each greenjob In Range("A4:A31")
For j = 4 To 31
For i = 4 To 10
Loop 1 goes through all of the rows on Worksheet One and identifies ones that need to be copied, so Loop 2 going through all of those rows again each time Loop 1 catches one doesn't make sense.
Instead, just use the Job Number from the row identified in Loop 1 and compare it to the Job Numbers on Worksheet 1 using Loop 3.
So, remove For j = 4 To 31 and Next j, and replace
If jobID.Cells(j, 1).Value = jobID2.Cells(i, 1).Value Then
with
If greenjob.Value = jobID2.Cells(i, 1).Value Then
since greenjob is, conveniently, the cell in column A that contains the job number.