Speeding up VBA Macro with multiple 'For' and 'if' statements - vba

This macro takes 2+ minutes to run. What are the best methods to optimize the macro?
Sub Time_Color(z, k)
Application.DisplayAlerts = False
For Each cell In Sheet1.Range("C" & z & ":" & "LZ" & z)
If cell.Value <> "x" Then
If cell.Value < Sheet3.Range("D" & k) Then
cell.Interior.ColorIndex = 37
cell.Offset(1, 0).Value = Sheet4.Range("D" & k).Value & "_" & Sheet4.Cells(k, 5).Value
End If
For j = 5 To 1000 Step 2
If cell.Value > Sheet3.Cells(k, j).Value And cell.Value < Sheet3.Cells(k, j + 1).Value Then
cell.Interior.ColorIndex = 37
cell.Offset(1, 0).Value = Sheet4.Cells(k, j + 1).Value & "_" & Sheet4.Cells(k, j + 2).Value
End If
Next j
For j = 4 To 1000 Step 2
If cell.Value >= Sheet3.Cells(k, j).Value And cell.Value <= Sheet3.Cells(k, j + 1).Value Then
cell.Interior.ColorIndex = 43
cell.Offset(1, 0).Value = Sheet4.Cells(k, j).Value & "_" & Sheet4.Cells(k, j + 1).Value
End If
Next j
End If
Next cell
Application.DisplayAlerts = True
End Sub
I am running this macro for 24 different combinations of z,k.

Try caching as much data as possible, for instance Sheet3.Range("D" & k) is constant throughout this function.
Every instance of the inner most loop will query that cell. If you put it at the beginning of this function, it will be looked up once and then used for the remainder of the function.
Edit:
In the comments on this question is - I think - a better answer by Tim Williams, which is specific to VBA:
Turn off ScreenUpdating and Calculation while running. Calculation
should be reset before your Sub ends (ScreenUpdating will reset
itself)

I'm not entirely sure what you are trying to accomplish, but it seems that your loop iterates over a large range to find the last-most instance of a cell that satisfies one of the two given criteria (your two loops).
If that is the goal, why not start from the back? Depending on how your sheet looks, this is potentially a lot faster!
I also made some other changes. Let me know how it works.
Take care to also include the function at the bottom (heisted from this answer), or substitute it for your function of choice.
Sub Time_Color(z, k)
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Dim loopVal, loopVal2, loopVal3 As Variant
Dim setOdd, setEven, OddEven As Boolean
Dim compVal, compVal2, compVal3 As Variant
compVal = Sheet3.Range("D" & k).Value
compVal2 = Sheet4.Range("D" & k).Value
compVal3 = Sheet4.Cells(k, 5).Value
For Each cell In Sheet1.Range("C" & z & ":" & "LZ" & z)
If cell.Value <> "x" Then
If cell.Value < compVal Then
cell.Interior.ColorIndex = 37
cell.Offset(1, 0).Value = compVal2 & "_" & compVal3
End If
For j = 1000 To 4 Step -1
loopVal = Sheet3.Cells(k, j).Value
loopVal2 = Sheet3.Cells(k, j + 1).Value
loopVal3 = Sheet4.Cells(k, j + 1).Value
OddEven = OddOrEven(j)
If OddEven = True Then
If cell.Value > loopVal And cell.Value < loopVal2 Then
cell.Interior.ColorIndex = 37
cell.Offset(1, 0).Value = loopVal3 & "_" & Sheet4.Cells(k, j + 2).Value
setOdd = True
End If
Else
If cell.Value >= loopVal And cell.Value <= loopVal2 Then
cell.Interior.ColorIndex = 43
cell.Offset(1, 0).Value = Sheet4.Cells(k, j).Value & "_" & loopVal3
setEven = True
End If
End If
If setEven = True And setOdd = True Then Exit For
Next j
End If
Next cell
Application.DisplayAlerts = True
End Sub
Function OddOrEven(a As Integer) As Boolean ' Returns TRUE if a is an odd number
If a - (2 * (Fix(a / 2))) <> 0 Then OddOrEven = True
End Function

Related

VBA code executes in break mode but skips to the end during normal run

This code works perfectly if I run it in break mode by stepping through each line. However, if I run it normally, it seems like it just skips to the end. It gives me a message box of a one second run time and none of the lines of code have been executed. Any help would be greatly appreciated!
Sub addVals()
Dim i As Integer, j As Integer, sheetName As String, timer As Double
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
timer = Now()
For i = 1 To 7
sheetName = Range("sheetnames2").Offset(i, 0).Value
For j = 1 To 3000
If Sheets(sheetName).Range("P" & j).Value <> 0 Then
For Each Cell In Range("R" & j, "R" & j + 30)
If Cell = 1 Then Range("S" & j).Value = Cell.Offset(0, -17).Value: Exit For
Next Cell
Else
End If
Next j
Next i
Application.Calculation = xlCalculationAutomatic
MsgBox (Format(Now() - timer, "HH:MM:SS"))
End Sub
Couple of corrections:
Sub addVals()
Dim i As Integer, j As Integer, sheetName As String, timer As Double
Dim sht As Worksheet, Cell As Range
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
timer = Now()
For i = 1 To 7
sheetName = Range("sheetnames2").Offset(i, 0).Value
With Sheets(sheetName) '<< scope the loop to the correct sheet
For j = 1 To 3000
If .Range("P" & j).Value <> 0 Then
For Each Cell In .Range("R" & j).Resize(30, 1).Cells
If Cell.Value = 1 Then
.Range("S" & j).Value = Cell.Offset(0, -17).Value
Exit For '<< unless you really meant what you wrote?
End If
Next Cell
End If
Next j
End With
Next i
Application.Calculation = xlCalculationAutomatic
MsgBox (Format(Now() - timer, "HH:MM:SS"))
End Sub
Note this one-liner:
If Cell = 1 Then Range("S" & j).Value = Cell.Offset(0, -17).Value: Exit For
is functionally the same as:
If Cell = 1 Then
Range("S" & j).Value = Cell.Offset(0, -17).Value
End If
Exit For
and is not the same as:
If Cell = 1 Then
Range("S" & j).Value = Cell.Offset(0, -17).Value
Exit For
End If
...so it might not be behaving as you expect

excel vba code corrupting my file consistently after a few runs

I have a relatively long set of subs that get run on a list of my excel files a few times a day. after a few runs, the file then becomes corrupted which normally would not be an issue b/c it doesn't really effect any of the data. however, I have another program that opens up each of the excel and pulls some key data from each one to make a summary sheet. because the corrupted file gives a message that says something along the lines of "there is a problem with some of your content" the summary program stops with a
run-time error '1004': Method of object 'Workbooks' Failed
I can not for the life of me figure out what in my code is causing the corruption. Is there a way I can have the summary code ignore the corruption notification? Ive tried a handful of different things including turning the application notifications off in my code to no avail.
Any help is greatly appreciated! ill post my all my code with a brief description below:
Here is the code from the summary file that opens each of the
individual files and pulls data:
Sub OEEsummmary()
Dim ActCycCell, ExpCycCell, ExpCurCycCell, ShiftCell, DifCell, DownCell, DTResACell, DTResBCell, PartCell, OpNamCell, OprCell, RejCell, RejResCell As Range
Dim MySheet As Worksheet
Dim Txt$, MyPath$, MyWB$
Dim myValue As Integer
Dim x As Long
Dim v As Variant, r As Range, rWhere As Range
MyPath = "L:\Manufacturing Engineering\Samuel Hatcher\"
x = 2
Set MySheet = ActiveSheet
'Application.ScreenUpdating = False
Application.EnableEvents = False
MySheet.Range("B2:G18").ClearContents
MySheet.Range("J2:O18").ClearContents
Do While MySheet.Range("A" & x).Value <> ""
MyWB = MySheet.Range("A" & x).Text
Workbooks.Open Filename:=MyPath & MyWB, ReadOnly:=True, IgnoreReadOnlyRecommended:=True
Set ActCycCell = ActiveSheet.Range("E21")
Set ExpCycCell = ActiveSheet.Range("D21")
Set ShiftCell = ActiveSheet.Range("E2")
Set DownCell = ActiveSheet.Range("K28")
Set DTResACell = ActiveWorkbook.Worksheets("Downtime").Range("O9")
Set DTResBCell = ActiveWorkbook.Worksheets("Downtime").Range("O10")
Set PartCell = ActiveSheet.Range("E4")
Set ExpCurCycCell = ActiveSheet.Range("D22")
If ActiveSheet.Range("I3") = "" Then
Set OpNamCell = ActiveSheet.Range("I2")
Else
Set OpNamCell = ActiveSheet.Range("I3")
End If
Set OprCell = ActiveSheet.Range("C4")
Set RejCell = ActiveSheet.Range("H21")
Set RejResCell = ActiveWorkbook.Worksheets("Rejected Parts").Range("H5")
With MySheet.Range("A" & x)
.Offset(0, 14).Value = OprCell.Value
.Offset(0, 13).Value = OpNamCell.Value
.Offset(0, 12).Value = PartCell.Value
.Offset(0, 11).Value = ShiftCell.Value
.Offset(0, 10).Value = RejResCell.Value
.Offset(0, 9).Value = RejCell.Value
.Offset(0, 6).Value = ActCycCell.Value
.Offset(0, 5).Value = ExpCycCell.Value
.Offset(0, 4).Value = ExpCurCycCell.Value
.Offset(0, 3).Value = DTResBCell.Value
.Offset(0, 2).Value = DTResACell.Value
.Offset(0, 1).Value = DownCell.Value
End With
ActiveWorkbook.Close savechanges:=False
x = x + 1
Loop
Call sort
'Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Clears the page of data to prepare it for a new shift of entering
data:
Sub ClearFrontEnd()
Sheets("Front End").Unprotect ("29745")
'prompts user to confirm if they realy want to clear entry
response = MsgBox("Are You Sure?", vbYesNo)
If response = vbNo Then
Exit Sub
End If
'checks to see if operator number is there
If range("I3").Value = "" Then
MsgBox "ENTER OPORATOR # AND CLICK NEW SHIFT AGAIN"
Else
Call StopTimer
Call prodChoose
Call transfer
Application.ScreenUpdating = False
ActiveWorkbook.Save
Sheets("Front End").Unprotect ("29745")
Sheets("Front End").Select
'Deletes the data from the entry and unique key fields
range("E8:E20").ClearContents
range("I8:I27").ClearContents
range("J8:J27").ClearContents
range("K8:K27").ClearContents
range("I3").ClearContents
range("H8").Value = ""
range("H9").Value = ""
range("H10").Value = ""
range("H11").Value = ""
range("H12").Value = ""
range("H13").Value = ""
range("H14").Value = ""
range("H15").Value = ""
range("H16").Value = ""
range("H17").Value = ""
range("H18").Value = ""
range("H19").Value = ""
range("H20").Value = ""
range("A1").Select
MsgBox "Please enter the correct values for SHIFT #, SHIFT LENGTH, PART #, AND OPORATOR #, Thanks! Have a great day!!"
End If
Sheets("Front End").Protect ("29745")
Call timerchoose
Application.ScreenUpdating = True
End Sub
This copies the data from the front page to a raw data sheet every
hour:
Sub transfer()
Sheets("Front End").Unprotect ("29745")
Application.ScreenUpdating = False
Dim x As Long
Dim v As Variant, r As range, rWhere As range
'set starting point at row 8
x = 8
'defines the sheet the data is being coppied from and pasted to
Dim sourceSheet As Worksheet: Set sourceSheet = ThisWorkbook.Worksheets("Front End")
Dim destSheet As Worksheet: Set destSheet = ThisWorkbook.Worksheets("Raw Data")
If sourceSheet.range("I3").Value = "" Then
Call StartTimer
Exit Sub
Else
Do While range("L" & x).Value <> ""
'Checks if the unique code is in the raw data sheet or not
v = sourceSheet.range("M" & x).Value
Set rWhere = destSheet.range("S:S")
Set r = rWhere.Find(what:=v, After:=rWhere(1))
If r Is Nothing Then
'selects the next row where the 1st column is empty
lMaxRows = destSheet.Cells(destSheet.Rows.Count, "A").End(xlUp).Row
'pastes the data from the specified cells into the next empty row
destSheet.range("A" & lMaxRows + 1).Value = sourceSheet.range("C2").Value
destSheet.range("M" & lMaxRows + 1).Value = sourceSheet.range("E2").Value
destSheet.range("N" & lMaxRows + 1).Value = sourceSheet.range("E4").Value
destSheet.range("P" & lMaxRows + 1).Value = sourceSheet.range("G4").Value
destSheet.range("Q" & lMaxRows + 1).Value = sourceSheet.range("C4").Value
destSheet.range("O" & lMaxRows + 1).Value = sourceSheet.range("I3").Value
destSheet.range("B" & lMaxRows + 1).Value = sourceSheet.range("J" & x).Value
destSheet.range("C" & lMaxRows + 1).Value = sourceSheet.range("K" & x).Value
destSheet.range("D" & lMaxRows + 1).Value = sourceSheet.range("L" & x).Value
destSheet.range("E" & lMaxRows + 1).Value = sourceSheet.range("I" & x).Value
destSheet.range("S" & lMaxRows + 1).Value = sourceSheet.range("M" & x).Value
x = x + 1
Else
x = x + 1
End If
Loop
x = 8
Do While range("D" & x).Value <> 0
If range("E" & x).Value <> "" Then
'Checks if the unique code is in the raw data sheet or not
v = sourceSheet.range("A" & x).Value
Set rWhere = destSheet.range("S:S")
Set r = rWhere.Find(what:=v, After:=rWhere(1))
If r Is Nothing Then
'selects the next row where the 1st column is empty
lMaxRows = destSheet.Cells(destSheet.Rows.Count, "A").End(xlUp).Row
'pastes the data from the specified cells into the next empty row
destSheet.range("A" & lMaxRows + 1).Value = sourceSheet.range("C2").Value
destSheet.range("M" & lMaxRows + 1).Value = sourceSheet.range("E2").Value
destSheet.range("N" & lMaxRows + 1).Value = sourceSheet.range("E4").Value
destSheet.range("P" & lMaxRows + 1).Value = sourceSheet.range("G4").Value
destSheet.range("Q" & lMaxRows + 1).Value = sourceSheet.range("C4").Value
destSheet.range("O" & lMaxRows + 1).Value = sourceSheet.range("I3").Value
destSheet.range("B" & lMaxRows + 1).Value = sourceSheet.range("B" & x).Value
destSheet.range("L" & lMaxRows + 1).Value = sourceSheet.range("C" & x).Value
destSheet.range("F" & lMaxRows + 1).Value = sourceSheet.range("D" & x).Value
destSheet.range("G" & lMaxRows + 1).Value = sourceSheet.range("E" & x).Value
destSheet.range("I" & lMaxRows + 1).Value = sourceSheet.range("G" & x).Value
destSheet.range("K" & lMaxRows + 1).Value = sourceSheet.range("H" & x).Value
destSheet.range("H" & lMaxRows + 1).Value = sourceSheet.range("N" & x).Value
destSheet.range("J" & lMaxRows + 1).Value = sourceSheet.range("O" & x).Value
destSheet.range("S" & lMaxRows + 1).Value = sourceSheet.range("A" & x).Value
x = x + 1
Else
x = x + 1
End If
Else
x = x + 1
End If
Loop
'sorts Raw Data table after new data is added
Dim ws As Worksheet
Set ws = ActiveWorkbook.Worksheets("Raw Data")
'specifies how to sort the data
With ws.Sort.SortFields
.Clear
.add Key:=ws.range("A2:A" & lMaxRows + 1), SortOn:=xlSortOnValues, _
Order:=xlAscending, DataOption:=xlSortNormal
.add Key:=ws.range("B2:B" & lMaxRows + 1), SortOn:=xlSortOnValues, _
Order:=xlAscending, DataOption:=xlSortNormal
'specifies range over which to sort
End With
With ws.Sort
.SetRange ws.range("A1:S" & lMaxRows + 1)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End If
Sheets("Front End").Protect ("29745")
Call SortDTWeek
Call SortDTMonth
Call StartTimer
Application.ScreenUpdating = True
End Sub
This checks a few cells constantly to see if they have been double
clicked, if so it puts the current time in that cell
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As range, cancel As Boolean)
'Adds downtime start and finish values
'Check to see if the click/selected cell is in columns I or J
If Not Intersect(Target, range("J:K")) Is Nothing Then
'Make sure cell is in range
If Target.Row > 7 And Target.Row <= 27 Then
'Update the value
Target.Value = Time()
End If
End If
End Sub
Checks to see if a set of cells has been changed, if so it puts the
now() value in a corresponding "key" column
Private Sub Worksheet_Change(ByVal Target As range)
Sheets("Front End").Unprotect ("29745")
Dim cell As range
'Adds unique keyA values
'Check to see if the changed cell is in column E
If Not Intersect(Target, range("E:E")) Is Nothing Then
For Each cell In Target.Cells
If cell.Value <> vbNullString And Target.Row > 7 And Target.Row <= 20 Then
'Update the "KeyA" value
Sheets("Front End").range("A" & Target.Row).Value = Now()
End If
Next cell
Else
'Adds unique keyB values
'Check to see if the changed cell is in column K
If Not Intersect(Target, range("K:K")) Is Nothing Then
For Each cell In Target.Cells
If cell.Value <> vbNullString And (Target.Row > "6" And Target.Row <= "27") Then
'Update the "KeyM" value
range("M" & Target.Row).Value = Now()
End If
Next cell
End If
End If
Sheets("Front End").Unprotect ("29745")
End Sub
thanks for any input this issue has been driving me crazy
as #MLind suggested in the comments, to bypass the corrupted file error and pull some data out i added this to my code:
Workbooks.Open Filename:=MyPath & MyWB, ReadOnly:=True, IgnoreReadOnlyRecommended:=True,
CorruptLoad:=xlExtractData
and used
Application.DisplayAlerts = False
within the loop to prevent any pop up boxes from stopping the sub

Repeat code in VBA

I have the following code that I need to repeat 1000 times:
Option Explicit
Sub Turn()
Range("f2").Select
If Range("e2").Value = "00/00/00" Then
ActiveCell.Value = 0
ElseIf Range("e2").Value Then
ActiveCell.Value = Range("e2")
End If
Range("f2").Select
If ActiveCell.Value > 0 Then
Range("G2") = Range("f2") - Range("b2")
End If
End Sub
I am new so I don't know if it is the most elegant solution to my problem, but it does the job. The problem is that I need the same code for 1000 rows and it seems a mighty task to change the cell number manually that many times.
Can You help me solve my problem?
I appreciate all help, thanks in advance.
you could achieve it using a 'for loop'. This should be on the right lines:
Option Explicit
Sub Turn()
Dim i As Long
For i = 2 to 1001
Range("f" & i).Select
If Range("e" & i).Value = "00/00/00" Then
ActiveCell.Value = 0
ElseIf Range("e" & i).Value Then
ActiveCell.Value = Range("e" & i)
End If
Range("f" & i).Select
If ActiveCell.Value > 0 Then
Range("G" & i) = Range("f" & i) - Range("b" & i)
End If
Next i
End Sub
Try it out and see where you get, let us know how it goes :)
This will be much quicker with an array:
Sub Recut()
Dim X, Y
Dim lngCnt As Long
X = [F2:G1001].Value2
Y = [B2:B1001].Value2
For lngCnt = 1 To UBound(X)
If X(lngCnt, 1) = "00/00/00" Then
X(lngCnt, 1) = 0
Else
If X(lngCnt, 1) > 0 Then X(lngCnt, 2) = X(lngCnt, 1) - Y(lngCnt, 1)
End If
Next
[F2:G1001].Value2 = X
End Sub
This is for your learning that you should avoid .Select in your code.
Pls have a look here
pls see the below simplified code.
Sub Turn()
Dim i As Long
For i = 2 To 1001
If Range("F" & i).Value = "00/00/00" Then
Range("F" & i).Value = 0
ElseIf Range("F" & i).Value > 0 Then
Ramge("G" & i).Value = Range("F" & i).Value - Range("B" & i).Value
End If
Next i
End Sub
Don't use "A1" style cell addresses but Cell(Row, Col) instead ...

VBA Loop not returning any values, plus failure to terminate

I have written a bit of code that is intended to search cells in a column, see if they start with a certain string and then return a value based on that string in another column. I have two problems, firstly the loops don't actually return any values in columns 8,9,10 or 11. Also the second loop doesn't stop running? Here is my code
Sub Possible_solution_one()
Dim i As Integer
Dim j As Integer
Dim ws As Worksheet
Set ws = ActiveSheet
For i = 2 To ws.Cells(ws.Rows.Count, "a").End(xlUp).Row
Do While Cells(i, 1).Value <> ""
If Cells(i, 2) = "Business://EXTRACTS/" & "*" Then
Cells(i, 8) = "OBS(" & Cells(i, 2).Value & ",SHARE,#DI) OR "
End If
i = i + 1
Loop
Next
For j = 2 To ws.Cells(ws.Rows.Count, "a").End(xlUp).Row
Do While Cells(j, 6).Value <> ""
If Cells(j, 6) = "Business" & "*" Then
Cells(j, 9) = "OBS(" & Cells(j, 4).Value & ",SHARE,DI) OR "
ElseIf Cells(j, 6) = "CSTM" Then
Cells(j, 10) = "PUM(" & Cells(j, 4).Value & ",#D7) OR "
ElseIf Cells(j, 6) = "*FS" Then
Cells(j, 11) = "FCON(" & Cells(j, 4).Value & ") OR "
End If
i = i + 1
Loop
Next
End Sub
To give the situation I have 1 type of string in column B and 3 types in column F. Am looking to return different things in columns 8,9,10,11 based on b and D
If you are pattern matching with wildcards, you need to use the Like operator. e.g. If Cells(i, 2) Like "Business://EXTRACTS/" & "*" Then
The Do While loops inside the For Next loops were unnecessary. It is also not a good idea to 'manually' increment the increment counter in a For ... Next loop.
The second loop was running forever because you were incrementing i, not j.
A Select Case statement would make a better fit for the multiple criteria in the second j loop.
Sub Possible_solution_one()
Dim i As Long, j As Long
Dim ws As Worksheet
Set ws = ActiveSheet
With ws
For i = 2 To .Cells(.Rows.Count, "a").End(xlUp).Row
If Not CBool(Len(Cells(i, "a").Value)) Then Exit For
If .Cells(i, 2) = "Business://EXTRACTS/" & "*" Then
.Cells(i, 8) = "OBS(" & .Cells(i, 2).Value & ",SHARE,#DI) OR "
End If
Next i
For j = 2 To .Cells(ws.Rows.Count, "a").End(xlUp).Row
Select Case .Cells(j, 6).Value
Case "Business*"
.Cells(j, 9) = "OBS(" & Cells(j, 4).Value & ",SHARE,DI) OR "
Case "CSTM"
.Cells(j, 10) = "PUM(" & Cells(j, 4).Value & ",#D7) OR "
Case "*FS"
.Cells(j, 11) = "FCON(" & Cells(j, 4).Value & ") OR "
End If
End Select
Next j
End With
End Sub
I've also incorporated a With ... End With statement to associate all of the cells to the parent ws worksheet. Note hte .Cells and not Cells. The prefixing period assigns parentage to the worksheet referenced in the With ... End With.
With no sample data, I could not completely test this rewrite but it does compile.
your second do while loop uses i=i+1 instead of j = j + 1 so it's not going to increment the cells(j, 6).value if there's anything in cells(j,6) then the loop won't stop running
For j = 2 To ws.Cells(ws.Rows.Count, "a").End(xlUp).Row
Do While Cells(j, 6).Value <> ""
If Cells(j, 6) = "Business" & "*" Then
Cells(j, 9) = "OBS(" & Cells(j, 4).Value & ",SHARE,DI) OR "
ElseIf Cells(j, 6) = "CSTM" Then
Cells(j, 10) = "PUM(" & Cells(j, 4).Value & ",#D7) OR "
ElseIf Cells(j, 6) = "*FS" Then
Cells(j, 11) = "FCON(" & Cells(j, 4).Value & ") OR "
End If
'i = i + 1
j = j + 1
Loop
Next

Merging over 2000 Cells using VBA?

I have wrote the following code to merge cells in excel, the data is around 26000 rows, the code is running on core I7 CPU with 8 GB RAM, the problem that it still working since 4 days, the average rows per day is 3000 row!, any one know how to get the result, because its a report that should be delivered since three days!
Sub MergeCellss()
lastRow = Worksheets("A").Range("A65536").End(xlUp).Row
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
For i = 2 To lastRow
If Cells(i, 2).Value <> Cells(i - 1, 2).Value And Cells(i, 2).Value <> Cells(i + 1, 2).Value Then
intUpper = i
Debug.Print ("<> -1 and <> +1 " & intUpper)
End If
If Cells(i, 2).Value <> Cells(i - 1, 2).Value And Cells(i, 2).Value = Cells(i + 1, 2).Value Then
intUpper = i
Debug.Print ("<> -1 and = +1 " & intUpper & " UPPPER LIMIT")
End If
If Cells(i, 2).Value <> Cells(i + 1, 2).Value And Cells(i, 2).Value = Cells(i - 1, 2).Value Then
Application.DisplayAlerts = False
Debug.Print ("<> +1 and = -1:" & i & "LOWER LIMIT")
DoEvents
For x = 1 To 8
Range(Cells(intUpper, x), Cells(i, x)).Merge
Next x
For j = 18 To 26
Range(Cells(intUpper, j), Cells(i, j)).Merge
Next j
Cells(intUpper, 14).Value = "=sumif(M" & CStr(intUpper) & ":M" & CStr(i) & ","">0"")"
Range(Cells(intUpper, 14), Cells(i, 14)).Merge
Range(Cells(i, 1), Cells(i, 26)).Borders(xlEdgeBottom).LineStyle = xlDouble
End If
If Cells(i, 2).Value <> Cells(i + 1, 2).Value And Cells(i, 2).Value <> Cells(i - 1, 2).Value Then
Debug.Print ("One Cells: " & i)
Range(Cells(i, 1), Cells(i, 26)).Borders(xlEdgeBottom).LineStyle = xlDouble
Cells(intUpper, 14).Value = Cells(intUpper, 13).Value
DoEvents
End If
Next i
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayAlerts = True
End Sub
the code above will merge the all cells containing repeated data like User Name, Date of Birth, .... into one cell, and leave the training courses and experiences as it is.
I wonder how can I run this code in less than 1 hour.
Here is some rewrite on your code. The two primary differences are the use of If ... ElseIf ... End If and the grouping of the first and fourth conditional operations (the conditions were the same).
Sub Merge_Cells()
Dim lastRow As Long, rw As Long
Dim intUpper As Long, x As Long
Dim vVALs As Variant
appTGGL bTGGL:=False
Debug.Print Timer
With Worksheets("A")
.Cells(1, 1) = Timer
lastRow = .Cells(Rows.Count, 1).End(xlUp).Row
For rw = 2 To lastRow
vVALs = Array(.Cells(rw - 1, 2).Value, .Cells(rw, 2).Value, .Cells(rw + 1, 2).Value)
If vVALs(1) <> vVALs(0) And vVALs(1) <> vVALs(2) Then
'the first and fourth conditions were the same so they are both here
'original first If condition
intUpper = rw
'Debug.Print ("<> -1 and <> +1 " & intUpper)
'original fourth If condition
'Debug.Print ("One Cells: " & rw)
.Range(.Cells(rw, 1), .Cells(rw, 26)).Borders(xlEdgeBottom).LineStyle = xlDouble
.Cells(intUpper, 14).Value = .Cells(intUpper, 13).Value
ElseIf vVALs(1) <> vVALs(0) And vVALs(1) = vVALs(2) Then
intUpper = rw
'Debug.Print ("<> -1 and = +1 " & intUpper & " UPPPER LIMIT")
ElseIf vVALs(1) = vVALs(0) And vVALs(1) <> vVALs(2) Then
'Debug.Print ("<> +1 and = -1:" & rw & "LOWER LIMIT")
For x = 1 To 26
If x < 9 Or x > 17 Then _
.Range(.Cells(intUpper, x), .Cells(rw, x)).Merge
Next x
.Cells(intUpper, 14).Value = "=sumif(M" & CStr(intUpper) & ":M" & CStr(rw) & ","">0"")"
.Range(.Cells(intUpper, 14), .Cells(rw, 14)).Merge
.Cells(rw, 1).Resize(1, 26).Borders(xlEdgeBottom).LineStyle = xlDouble
End If
Next rw
.Cells(1, 2) = Timer
End With
Debug.Print Timer
appTGGL
End Sub
Sub appTGGL(Optional bTGGL As Boolean = True)
Application.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
Application.ScreenUpdating = bTGGL
Application.EnableEvents = bTGGL
Application.DisplayAlerts = bTGGL
End Sub
I've also read the three primary conditional values into a variant array to reduce repeated worksheet value reads.