For the first time I've worked in Excel VBA to find rows in my dataset that contain the same adress as another entry in a cluster. These entries have to be merged and the row then is deleted. I've come up with the following, which works (As far as I can tell from the testing I did on small samples of the set):
Sub Merge_Orders()
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Dim lastrow As Long
lastrow = Cells(Rows.Count, "A").End(xlUp).Row
Dim y As Long
Dim x As Long
Dim j As Long
Dim k As Long
For i = 2 To lastrow //for each row, starting below header row
j = 1
y = (Cells(i, 9)) //this is the clusternumber
Do While y = (Cells(i + j, 9)) //while the next row is in the same cluster
x = (Cells(i, 12)) //this is the adresscode
k = 1
Do While x = (Cells(i + k, 12)) //while the current adresscode is the same as the next, iterating until another adresscode is hit
Cells(i, 16) = Val(Cells(i, 16)) + Val(Cells(i + k, 16)) //update cell value
Cells(i, 18) = Cells(i, 18) + Cells(i + k, 18) //update cell value
Cells(i, 19) = Cells(i, 19) + Cells(i + k, 19) //update cell value
If Cells(i, 20) > Cells(i + k, 20) Then
Cells(i, 20) = Cells(i + k, 20) //update cell value
End If
If Cells(i, 21) > Cells(i + k, 21) Then
Cells(i, 21) = Cells(i + k, 21) //update cell value
End If
Cells(i, 22) = Cells(i, 22) + Cells(i + k, 22) //update cell value
Cells(i, 23) = Cells(i, 23) + Cells(i + k, 23) //update cell value
Rows(i + 1).EntireRow.Delete //Delete the row from which data was pulled
k = k + 1
Loop
j = j + 1
Loop
Next i
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
End Sub
The problem I'm facing is time. Testing this on a small sample of ~50 rows took over 5 minutes. My entries total over 100K rows. It's been running for over a day with no end in sight. Is there a way to optimize this so I don't have to wait until I'm grey?
Kind regards,
Rob
Two things as I mentioned in the comments:
1) Remove k (and the entire k=k+1 line); replace with j. Also replace your Rows(i + 1).EntireRow.Delete with Rows(i + j).EntireRow.Delete.
2) Since you are deleting rows, lastrow is actually blank by the time you get there. Instead of i=2 to lastrow, make it do while Cells(i,12)<>"" or something. This is causing it to loop over a bunch of rows that are empty.
Also, you can do these type of rollups much easier with a PivotTable, or, as mentioned in the comments, with an SQL GROUP BY.
Related
how to copy a range of cells(N cells) from a column in one sheet and paste it, with a blank cell inserted after every 7(n cells), to another sheet?
Ex: Given image below
I think it's a pretty easy task and you are making it sound difficult. Here's the code to help you out.
Sub do_something()
last_row = ThisWorkbook.Worksheets("Sheet1").Cells(1, 1).End(xlDown).Row
j = 1
For i = 1 To last_row
If i Mod 8 = 0 Then
j = j + 1
End If
Cells(j, 2).Value = Cells(i, 1).Value
j = j + 1
Next i
End Sub
And as you mentioned in your comment, below code will satisfy your need.
Sub do_something2()
last_row = ThisWorkbook.Worksheets("Sheet1").Cells(1, 1).End(xlDown).Row
j = 1
i = 1
Do While i <= last_row
Worksheets("Sheet2").Range(Worksheets("Sheet2").Cells(j, 1),
Worksheets("Sheet2").Cells(j + 6, 1)).Value = _
Worksheets("Sheet1").Range(Worksheets("Sheet1").Cells(i, 1),
Worksheets("Sheet1").Cells(i + 6, 1)).Value
j = j + 8
i = i + 7
Loop
End Sub
I have the following VBA code that counts how many rows there are in a table and then extracts data from every other row (spreadsheet A). I have another spreadsheet (spreadsheet B) that grabs data from the output of this code using Match and Indirect formulas. When I run the code with only spreadsheet A open it runs very fast. If I have spreadsheet B open while I run the code in spreadsheet A it runs a lot slower, I'm guessing because it's trying to grab data while the code is running. Is there a way I can have the code run and stop the other spreadsheet from accessing the data until the code is finished? It's inconvenient to close spreadsheet B every time i need to run the code.
Sub GetTopForces()
Application.ScreenUpdating = False
Dim i, j, count, cell As Integer
count = 0
cell = 1
i = 0
j = 0
' get count
Do While cell <> 0
cell = Cells(count + 4, 1).Value
count = count + 1
Loop
' get forces on top of column only
Do While i < count
Cells(4 + i, 16).Value = Cells(5 + j, 1).Value
Cells(4 + i, 17).Value = Cells(5 + j, 2).Value
Cells(4 + i, 18).Value = Cells(5 + j, 3).Value
Cells(4 + i, 19).Value = -1 * Cells(5 + j, 5).Value
Cells(4 + i, 20).Value = Cells(5 + j, 9).Value
Cells(4 + i, 21).Value = Cells(5 + j, 10).Value
i = i + 1
j = j + 2
Loop
Application.ScreenUpdating = True
End Sub
My knowledge in VBA is basic so I appreciate any advice. Thank you :)
Here is a reference image of my worksheet so that everyone can see the format.
http://imgur.com/a/dacIB
The purpose of this is to sort data that matches into columns. The criteria that I'm looking for is on the right and the database data that i'm looking through is on the left. Here is my code for the loop.
Dim i As Long
Dim Counter As Long
Dim WS_Count As Long
Dim k As Long
WS_Count = Worksheets.Count
For k = 4 To WS_Count
With Worksheets(k)
For Counter = 0 To ActiveSheet.Rows(1).Cells.Find("QQQ").Offset(0, -1) - 1
For i = 0 To ActiveSheet.Range("A" & ActiveSheet.Rows.Count).End(xlUp).Row - 2
If Cells(2 + i, 5).Value = Rows(i + 2).Cells.Find("QQQ").Offset(0, 1) And _
Cells(2 + i, 2).Value = Rows(i + 2).Cells.Find("QQQ").Offset(0, 3) And _
Cells(2 + i, 1).Value = Rows(i + 2).Cells.Find("QQQ").Offset(0, 2) Then
Cells(2 + i, Counter + 7).Value = Cells(2 + i, 4).Value
End If
Next i
Next Counter
End With
Next k
I need to get the Value in column D into columns associated with the criteria on the right. Columns G:O, numbered 1-9, match the column T numbers, 1-9.
I can't for the life of me figure out why, in Row 4, that it made 0's all the way across. It should go in this order all the rows in the first column > all the rows in the second column > ... > next sheet. If anything is unclear please let me know.
Edit: So my Counter and i Longs were slightly off so I made some adjustments. They should be right, but my code still isn't executing correctly. It is not seeing the matches it should. My If Then must be messed up some how.
Dim i As Long
Dim Counter As Long
Dim WS_Count As Long
Dim k As Long
WS_Count = Worksheets.Count
For k = 4 To WS_Count
With Worksheets(k)
For Counter = 0 To .Rows(1).Cells.Find("QQQ").Offset(0, -1).Value - 1
For i = 0 To .Range("A" & .Rows.Count).End(xlUp).Row - 2
If .Cells(2 + i, 5).Value = .Rows(Counter + 2).Cells.Find("QQQ").Offset(0, 1) And _
.Cells(2 + i, 2).Value = .Rows(Counter + 2).Cells.Find("QQQ").Offset(0, 3) And _
.Cells(2 + i, 1).Value = .Rows(Counter + 2).Cells.Find("QQQ").Offset(0, 2) Then
.Cells(2 + i, Counter + 7).Value = .Cells(2 + i, 4).Value
End If
Next i
Next Counter
End With
Next k
Okay, so it works now. I think it was having a really hard time figuring out what sheet to pull the statements from. Notice the .Cells(..... That period made the Cells defined to the ActiveSheet. I also changed .Rows(i+2)... to .Rows(Counter+2) The criteria cell location would shift down with each new imaking it impossible for there to be a match. The one in the screenshot just happened to be coincidence. Thanks, hope this helps someone in the future.
I'm trying to run a For Next Loop until the last row of a specific column (but not the last row of the sheet). So the first part of my list has data in column F and the second part doesn't. I only want the macro to apply to that first part. For some reason the loop only runs through the first part with certain commands but doesn't with the ones I am trying to do now. (I know it would be easy just to seperate the two parts manually and then run it but it drives me nuts not knowing what it is I did wrong :)).
This is the code:
Dim i As Integer
Dim g As Double
g = 0.083333333
Dim lastrow As Long
lastrow = Sheets("zm").Range("f" & Rows.Count).End(xlUp).Row
Sheets("zm").Activate
For i = 2 To lastrow
If Sheets("zm").Cells(i, 1) = Sheets("zm").Cells(i + 1, 1) And Sheets("zm").Cells(i, 5) = Sheets("zm").Cells(i + 1, 5) And Sheets("zm").Cells(i + 1, 6) - Sheets("zm").Cells(i, 7) < g Then
Sheets("zm").Cells(i + 1, 7).Copy
Sheets("zm").Cells(i, 7).Select
ActiveSheet.Paste
Application.CutCopyMode = False
Sheets("zm").Rows(i + 1).Delete
End If
Next i
Thanks for your help!
avoid Select/Selection and/or Activate/ActiveXXX
try this:
Option Explicit
Sub main()
Dim i As Long, lastrow As Long
Dim g As Double
g = 0.083333333
With Worksheets("zm")
lastrow = .Cells(.Rows.Count, "F").End(xlUp).Row
For i = lastrow To 2 Step -1
If .Cells(i, 1) = .Cells(i + 1, 1) And .Cells(i, 5) = .Cells(i + 1, 5) And .Cells(i + 1, 6) - .Cells(i, 7) < g Then
.Cells(i + 1, 7).Copy Destination:=.Cells(i, 7)
.Rows(i + 1).Delete
End If
Next i
End With
End Sub
I need to copy a lot of rows. I tried to do something.copy something else.paste but it's extremely slow
I tried to do
Range(..).value(formula) = Range(..).value(formula) but its not so good because i have a date there that turn to ######
I need a faster way to do this copy/paste
This is my code:
Function Last_Col(k As Long) As Long
Last_Col = Cells(k, Columns.Count).End(xlToLeft).Column
End Function
Function Last_Col_Doc() As Long
Last_Col_Doc = Cells.SpecialCells(xlCellTypeLastCell).Offset(1, 0).Column
End Function
Function Is_Grouped(i As Long) As Boolean
Is_Grouped = (Cells(i, 2).Rows.OutlineLevel > 1)
End Function
Function Is_Bold(i As Long) As Boolean
Is_Bold = Cells(i, 2).Font.Bold
End Function
Function Print_NA(i As Long, k As Long) As Boolean
Range(Cells(i, 21), Cells(i, 21 + k - 2)).Value = "NA"
End Function
Function Last_Row() As Long
Last_Row = Cells(Rows.Count, "B").End(xlUp).Row
End Function
Sub EditParanoia()
Dim FrstBlkRow As Long
Dim flag As Boolean
Dim i As Long
Dim HeadLen As Long
FrstBlkRow = Last_Col(1) + 1
If FrstBlkRow < 25 Then 'first edit
flag = True
i = 2
Do While flag
If Is_Bold(i) Then
flag = False
Else
i = i + 1
End If
Loop
HeadLen = Last_Col(i)
Range(Cells(i, 2), Cells(i, HeadLen)).Copy
Range(Cells(1, FrstBlkRow), Cells(1, FrstBlkRow + HeadLen - 2)).PasteSpecial
Else
FrstBlkRow = 21
HeadLen = 10
End If
Dim j As Long
For i = 2 To Last_Row Step 1
If Not Is_Grouped(i) And Not Is_Grouped(i + 1) And Cells(i, FrstBlkRow + 1).Value = vbNullString Then
'if not part of group
Range(Cells(i, FrstBlkRow), Cells(i, FrstBlkRow + HeadLen - 2)).Value = "NA"
ElseIf Not Is_Grouped(i) And Is_Grouped(i + 1) And Is_Grouped(i + 2) And Not Is_Grouped(i + 3) Then
'if Part of group of 1 val
Range(Cells(i + 2, 2), Cells(i + 2, 2 + HeadLen - 2)).Copy
Range(Cells(i, FrstBlkRow), Cells(i, FrstBlkRow + HeadLen - 3)).PasteSpecial
ElseIf Not Is_Grouped(i) And Is_Grouped(i + 1) Then
'if part of group of more then one val
j = 1
Do Until Is_Grouped(i + j) And Not Is_Grouped(i + j + 1)
'j will get the langth of any group
j = j + 1
Loop
'past the relevant cell in the right place
Range(Cells(i + 2, 2), Cells(i + 2 + j - 1 - 1, 2 + HeadLen - 2)).Copy
Range(Cells(i, FrstBlkRow), Cells(i + j - 1 - 1, FrstBlkRow + HeadLen - 3)).PasteSpecial
'past the head respectively
Range(Cells(i, 1), Cells(i, 20)).Copy
Range(Cells(i + 1, 1), Cells(i + j - 2, FrstBlkRow - 1)).PasteSpecial
End If
Next
End Sub
When you say you've tried "Range(..).value(formula) = Range(..).value(formula)", what do you mean? You should be able to set two ranges equal to eachother:
Say A1:A10 has "Batman, 10-01-2015" and you want to copy that range to B1:B10, Range("B1:B10").Value = Range("A1:A10").Value. You can't do that? I tried it with dates, and it set the B range values to dates, no reformatting necessary.
I also notice in your code, you PasteSpecial, but don't specify what type of special paste. See the Microsoft (or this one) page for more info.