Find next ocurrence of values - COUNTIF is too slow - excel

I'm trying to speed up a COUNTIFS formula in a table I have.
The table is over 60k rows and the COUNTIFS has three conditions. The formula right now looks like this:
=IF(AND(COUNTIFS([Vessel],[#Vessel],[Date],">"&[#Date],[ETA],"<="&[#ETA]+20)=0,[#Arrived]=1,[#Sailed]=1,[#Date]<MAX([Date])),1,0)
The problem is that the calculation takes a very long time and it triggers everytime something change, even the filter. I don't want to turn calculations to manual in this sheet.
The purpose of the formula is to find the next occurence of the vessel in the line, the ETA can be slightly changed from day to day or the same ship can appear months later. I need to confirm if the vessel appears with the same ETA (or up to 20 days of difference) on another day.
Is there any other solution to this problem?

Maybe try building this as a macro instead, that way you would have control over when it executes.
Here is a start on a method for doing this. It gets the job done but breaks on an error on/after the last line is processed. Edit : Fixed and tested
Public Sub shipcheck()
Application.ScreenUpdating = False
Dim x As Long
Dim y As String
Dim counter As Long
For x = 2 To Range("A85536").End(xlUp).Row ' Assuming data has headers
y = Cells(x, 1) ' This assumes your vessel is in the first column
counter = x + 1
Do
If cells(counter,1) = "" Then Exit Do
If y = Cells(counter, 1) Then
If Cells(x, 2) <> Cells(counter, 2) Then 'This assumes your date is the second column
If DateDiff("d", Cells(x, 3), Cells(counter, 3)) > 20 Then ' this assumes ETA is your third column
Cells(x, 4) = 1 'This assumes the positive test is the fourth column
Cells(counter, 4) = 1
Exit Do
Else
End If
Else
End If
Else
End If
counter = counter + 1
Loop
Next x
Application.ScreenUpdating = True
End Sub

Related

VBA: Delete row if value is in list, looping through list

I have two tables. One table is called DRData (Blad3), other table is CheckData (Blad2). EANCODE is Column J for DRData, and Column A for Checkdata.
I want to check whether CheckData.EANCODE is present in DRData.EANCODE. If so; delete that row from CheckData.
I tried several things, but no success yet. The code I have written now is as follows:
Sub FindEAN()
Dim i As Long
Dim x As Long
x = 1 'Start on first row
EANtoFind = Blad2.Range("A" & x).Value
For i = 1 To 99999 '
If Blad3.Cells(i, 1).Value = EANtoFind Then
Blad2.Range("A" & x).EntireRow.Delete
Else: x = x + 1
End If
Next i
End Sub
When the EANCODE is not present, I want to hop over a row to check that code. I want to end with a list in CheckData where all the EANCODE values that are not in DRData are shown.
With the code above, only the first row is getting deleted and now I'm stuck how to get this to loop. Including the x+1 to get to the next row.
First you have to clarify that your problem is a little bit complicated. You have to pay attention to indexes when deleting rows
To do that, you have to point the the maximal number of line to optimize your loop
A simple way to do that, is to use predefined search function, and edit your code a little bit.
My favorite is Application.match(), which takes 3 parameters :
Value to look for
Array where you look (in our case the column J of Blad3 or position 10)
0 : exact match
For more details, see the documentation
https://learn.microsoft.com/fr-fr/office/vba/api/excel.worksheetfunction.match
An example of code which works is like the following
Sub FindEAN()
Dim x As Long
x = 1 'Start on first row
maxline = Blad2.Range("A" & Rows.count).End(xlUp).row
While x <= maxline '
EANtoFind = Blad2.Range("A" & x).Value
If Not IsError(Application.Match(EANtoFind, Blad3.Columns(10), 0)) Then
Blad2.Range("A" & x).EntireRow.Delete
maxline = maxline - 1
Else
x = x + 1
End If
Wend
End Sub

VBA Code to add first 10 even numbers regardless of number of inputs in a column

I ran into a problem when I try to add the first 10 even numbers in a column regardless of the number of inputs someone has entered into said column.
The issue occurs when there are less than 10 inputs (in my case 7) and I have tried to break the loop if there are no more numbers after the last one but it doesn't seem to work as it crashes Excel; most probably because it loops infinitely.
The original code was fine until I entered below 10 even numbers. When I did it would loop infinitely and crash so I inputted a forceful break in the code (hence the Count=999) but it does not seem to work
Sub TenPosInt()
Dim Total As Integer, size As Integer, myRange As range
Dim Count As Integer
Count = 1
Set myRange = range("W:W")
size = WorksheetFunction.CountA(myRange)
While Count <= 10
If IsEmpty(Cells(Count, "W")) Then
Count = 999
End If
If Cells(Count, "W").Value Mod 2 = 0 Then
Total = Total + Cells(Count, "W").Value
Count = Count + 1
End If
Wend
MsgBox Total
End Sub
My Inputs are currently 2,4,6,5,2,4,6,8,1,3,5 so it does not meet the 10 even integers, however I still want it to run regardless (hence the Count=999 line). The correct return should be 32.
A Do-While/Until loop is recommended instead of While-Wend (see this).*
Here I use a separate counter for row and the number of even values (and stole David's idea of combining the two conditions in the Do line).
Sub TenPosInt()
Dim Total As Long, r As Long, Count As Long
r = 1
Do Until Count = 10 Or Cells(r, "W") = vbNullString
If Cells(r, "W").Value Mod 2 = 0 Then
Total = Total + Cells(r, "W").Value
Count = Count + 1
End If
r = r + 1
Loop
MsgBox Total & " (" & Count & " even numbers)"
End Sub
*Actually I would be more inclined to use one of the other gent's answers, but I have tried to stick as close to yours as possible. (Also a good idea to check a cell is numeric before checking for even-ness.)
Just for fun - here is an approach that uses a For...Next loop, allows for non-numeric entries in Column W, and handles the possibility of blank rows between entries.
Sub TenPosInt()
Dim lastRow As Long
lastRow = Cells(Rows.Count, "W").End(xlUp).Row
Dim i As Long
For i = 1 To lastRow
If Not IsEmpty(Cells(i, "W")) Then
If IsNumeric(Cells(i, "W")) Then
If Cells(i, "W").Value Mod 2 = 0 Then
Dim counter As Long
counter = counter + 1
Dim total As Long
total = total + Cells(i, "W").Value
If counter = 10 Then Exit For
End If
End If
End If
Next
MsgBox total
End Sub
Why not use a standard for loop across a range? this would give more specific inputs for the subroutine.
Description of what is occuring below has been commented out to allow for copy/pasting more easily.
'Define your range (you use columns("W"), but narrow that)... assuming you start in row 2 (assumes row 1 is headers), move to the last row, of the same columns:
lr = cells(rows.count,"W").end(xlup).row
'so you know the last row, loop through the rows:
for i = 2 to lr
'Now you will be doing your assessment for each cell in column "W"
if isnumeric(cells(i,"W").value) AND cells(i,"W").value mod 2 = 0 then
s = s + cells(i,"W").value
counter = counter + 1
if counter = 10 then exit for
end if
'Do that for each i, so close the loop
next i
'You now have determined a total of 10 items in the range and have added your items. Print it:
debug.print s
Edit1: got a comment to not break-up the code in an explanatory fashion, so I have added ' to comment out my explanations in an effort to make my coding portion copy/pasteable as a lump.

Simple VBA script to flag transactions over $4000 that follow a pattern

I'm trying to write a simple script that compares and flags similar transactions (rows) and pastes them at the bottom of the sheet. The transactions which are to be flagged should meet the following criteria.
The $amount in the transactions is greater than 4000 or less than -4000 (column 11)
The two transactions being compared have the same part number (column 3)
Two transactions with similar dollar amounts (between 90-110% of each other) and opposite in number sign
Sub checktrans()
Dim newLastRow, rowcount As Long
Dim row, row2, amountcol, partnumcolcol As Integer
amountcol = 16
partnumcol = 3
rowcount = 27307
newLastRow = 37309
For row = 1 To rowcount
For row2 = 1 To rowcount
If Cells(row, amountcol) > 4000 Or Cells(row, amountcol) < -4000 Then
If row <> row2 Then
If Cells(row, partnumcol) = Cells(row2, partnumcol) Then
If Abs(Cells(row, amountcol)) > 0.9 * Abs(Cells(row2, amountcol)) And Abs(Cells(row, amountcol)) < 1.1 * Abs(Cells(row2, amountcol)) Then
If (Cells(row, amountcol) < 0 And Cells(row2, amountcol) > 0) Or (Cells(row, amountcol) > 0 And Cells(row2, amountcol) < 0) Then
ActiveSheet.Rows(row).Copy
ActiveSheet.Rows(newLastRow).PasteSpecial xlPasteAll
newLastRow = newLastRow + 1
ActiveSheet.Rows(row2).Copy
ActiveSheet.Rows(newLastRow).PasteSpecial xlPasteAll
newLastRow = newLastRow + 1
End If
End If
End If
End If
End If
Next row2
Next row
End Sub
I wrote the code above, and it seems to work for a low number of rows (below 500), but when the number of rows exceeds 27000 it goes into a never ending loop that keeps pasting new rows onto the sheet. It also posts each couple of transactions twice, which I understand is flaw in the logic which I have to work out as well.
P.S I am a giant noob when it comes to this, haven't programmed much before, and I'm just learning now to make my life easier.
First thing you can do is to start second loop from the point where first loop is currently. Like For row2 = row + 1 to rowcount. You checked previous records already. This will also fix problem with duplicates and you can delete If row <> row2.
Second, is to use Application.ScreenUpdating = False at the beginning of the macro and Application.ScreenUpdating = True at the end. This turns off screen updating while your macro is running and can be a huge improvement in performance.
At the end you can join all Ifs into one using And, however I don't know if this will improve performance.

looping through rows and adding values

Here is my code
With Worksheets("Sheet1")
For x = 5 To x = 30 Step 1
Set addRange1 = .Range(.Cells(x, 4), .Cells(x, myCount2))
cell(x, myCount2 + 2).Value = Application.Sum(addRange1)
Next
End With
First row would be cell(5,4) to cell(5, 20) or something. Sum the values of that row and display it in cell (5,22)
Repeat up to x = 30
Hope that is clear
Getting blank cells where sum value should be
This would be correct:
With Worksheets("Sheet1")
For x = 5 To 30
Set addRange1 = .Range(.Cells(x, 4), .Cells(x, myCount2))
Cells(x, myCount2 + 2).Value = Application.Sum(addRange1)
Next
End With
The For ... Next was incorrect. Instead of To x = 30 it has to be To 30.
Info what happened here: If you walked through your code step-by-step you would have noticed that everything inside your For ... Next is never executed. This is because x = 30 in the For sets x to the value 30 and this is the end of the For so the For gets never executed.
The Step 1 isn't necessary, it is 1 by default you may remove it or keep it but it is not necessary.
And as Darren Bartrup-Cook mentioned it has to be Cells instead of Cell.

Subtract Amount From Cell Until It Reaches 0 multiple reminding amount

My formula below subtracts a cell until it reaches zero, and moves to the next one. The subtraction is based on the value “B”. Each time the formula comes across the value “B”, this action is performed.
Question: I have been trying to advance this to formulae, in that each time “B” is found that cell is minuses until zero and those amount multiple by the adjacent price.
could you please provide me with a formula which does this ?
Example: when it comes across the first B the full value of 100 x 10 will be multiplied and the reminder 50 will be multiplied by 15 i.e. 50 x 15 price of the next A. These values will be summed.
=MAX(SUMIF($A$2:A2,"A",$B$2:B2)-SUMIF($A$2:$A$10,"B",$B$2:$B$10),0)
The reminder of the 50 is coming from the difference between the B 150 - A 100 , which leaves 50 to be still absorbed .
Further Calculation for explanation:
Apologies thats meant to say calculation of 6000
Your question is still very unclear. What does "My formula below subtracts a cell until it reaches zero" mean? Also, as OldUgly pointed out, it seems that you are ignoring the second A. Since we can't understand each other, take a look at the code below and try to rewrite it yourself to fit your needs. It assumes the data is in a sheet named "Data", and that there is a button (Button1) to run the code.
Dim lLastRow As Long
Dim i As Integer
Dim QtyNumberA, QtyNumberB, QtyNumberRem As Integer
Sub Button1_Click()
lLastRow = Worksheets("Data").Cells(2, 1).End(xlDown).Row 'Rows with data, starting 2nd row (titles in the first)
QtyNumberA = 0 'Variable for storing quantities of A
QtyNumberB = 0 'Variable for storing quantities of B
QtyNumberRem = 0 'Variable for storing quantities remaining
For i = 2 To lLastRow 'scan all rows with data
If (Worksheets("Data").Cells(i, 1).Value = "A") Then
QtyNumberA = QtyNumberA + Worksheets("Data").Cells(i, 2).Value
ElseIf (Worksheets("Data").Cells(i, 1).Value = "B") Then
QtyNumberB = QtyNumberB + Worksheets("Data").Cells(i, 2).Value
QtyNumberRem = QtyNumberA - QtyNumberB
Worksheets("Data").Cells(i, 6) = QtyNumberRem
End If
Next
End Sub

Resources