VBA search for corresponding line and compare values - vba

I am new to VBA programming, and I am looking for a way to search a range of about 2,000 to 3,000 rows to compare quantities where ID numbers match, and generate a message box displaying the ID number if there are any matching ID numbers where quantities do not match. There are 2 matching ID numbers in the data.
I have found and adapted this code
`Dim rng1 As Range, rng2 As Range, rngName As Range, i As Integer, j As Integer
For i = 1 To Sheets("Sheet1 (2)").Range("q" & Rows.Count).End(xlUp).Row
Set rng1 = Sheets("Sheet1 (2)").Range("q" & i)
For j = 1 To Sheets("Sheet1 (2)").Range("q" & Rows.Count).End(xlUp).Row
Set rng2 = Sheets("Sheet1 (2)").Range("q" & j)
Set rngName = Sheets("Sheet1 (2)").Range("q" & j)
If rng1.Value = rng2.Value Then
If rng1.Offset(0, 2).Value <> rng2.Offset(0, 2).Value Then
MsgBox ("Not equal " & rng1 & " Net " & rng1.Offset(0, 2) - rng2.Offset(0, 2))
Exit For
End If
End If
Set rng2 = Nothing
Next j
Set rng1 = Nothing
Next i`
Honestly, this is beyond my current skill level in VBA, and I can't think of any way to use the Macro recorder to help. I want to know if there is any way to optimize this code to run faster, and also if there is a way to adapt it to write out all ID numbers with the amount of variance, instead of displaying the message box for each ID number individually.
Thanks!

If the data is sorted (assumes ID in column Q, quantity in Column R):
Dim ErrorList as String
With Sheets("Sheet1 (2)")
For i = 1 To .Range("q" & Rows.Count).End(xlUp).Row step 2
if .range("R" & i).value <> .range("R" & i+1) then
ErrorList = ErrorList & "ID: " & .range("Q" & I) & " Net: " & _
.range("R" & i).value - .range("R" & i+1).value & vbcrlf
end if
next
end with
'do something like MsgBox with ErrorList
If the data is NOT sorted (same column assumptions):
Dim ErrorList as String
With Sheets("Sheet1 (2)")
For i = 1 To .Range("q" & Rows.Count).End(xlUp).Row
'assume .range("q"
Set Rng = .Range("q:q").Find(what:=.range("q" & i), LookIn:=xlValues, _
lookat:=xlPart, MatchCase:=False)
If .range("Q" & I).value <> rng.cells(1,1).value then
ErrorList = ErrorList & "ID: " & .range("Q" & I) & " Net: " & _
.range("R" & i).value - .range("R" & i+1).value & vbcrlf
end if
next
end with
'do something like MsgBox with ErrorList
The .Find is MUCH quicker than an inner loop through all the rows of data again (someone a week or so ago tested and found something along the lines of 1000x times faster than a loop). Unfortunately, this method will leave you with duplicate IDs in your mismatch list, since it will run through the whole list, finding each of the pairs and discovering that they both don't match:
ID Value
ABC 1
BCD 6
ABC 2
It will loop to ABC/1, find ABC/2, then later in the loop find ABC/2 and discover that it doesn't match ABC/1, and report on both. The sorted data in the first option will work better if you can get your data sorted.
NOTE: No code was tested in the writing of this answer. There may be typos to resolve

You can do this with a formula if there can be at most only two matches: eg if ID is in ColA and Amount in ColB then in ColC enter
=IFERROR(VLOOKUP(A2,A3:B$9,2,FALSE)-B2,0)
and fill down. Result will be zero if no mismatch (or no matching Id), otherwise will be the difference between the two amounts.

Related

Concatenate two ranges without looping

I want to concatenate two ranges into one WIHTOUT using a loop.
Below is the code. The lines with comment's is basically the solution I want to avoid.
With ws_AUoM
lCountEntriesInAUoMFile = .Cells(Rows.Count, "B").End(xlUp).Row
.Range("O2:O" & lCountEntriesInAUoMFile).Value = .Range("B2:B" & lCountEntriesInAUoMFile).Value & .Range("F2:F" & lCountEntriesInAUoMFile).Value
' For lLoopCounterAUoM = 2 To lCountEntriesInAUoMFile
'
' .Cells(lLoopCounterAUoM, "O").Value = .Cells(lLoopCounterAUoM, "B").Value & .Cells(lLoopCounterAUoM, "F").Value
'
' Next lLoopCounterAUoM
End With
This line:
.Range("O2:O" & lCountEntriesInAUoMFile).Value = .Range("B2:B" & lCountEntriesInAUoMFile).Value & .Range("F2:F" & lCountEntriesInAUoMFile).Value
returns the error "Type Mismatch". I have double checked the sizes and location of each range. Yet it does not work. What am I missing here?
You can do this:
Dim r As Long
With ws_AUoM
r = .Cells(.Rows.Count, "B").End(xlUp).Row
.Range("O2:O" & r).Value = .Evaluate("B2:B" & r & " & F2:F" & r)
End With
Evaluate knows you're giving it an array formula, and will return the resulting array, which you can assign directly to the sheet.

Decreasing complexity of loop & Find- VBA

I have written the following code; both Sheet 1 and Sheet 2 are rather big sheet with lots of data. Run this Macro is extremely time comsuming ( It has a high complexity I guess). I'm quite new at VBA and therefore having trouble make the code more effective.
Sub Find()
Dim rgFound As Range
Dim Index As Long: Index = 6
Dim Row as Long
Do While Worksheets("Sheet2").Cells(Index, "D").Value > 0
Sheets("Sheet1").Select
Set rgFound = Range("A1:A20000").Find(Worksheets("Sheet2").Cells(Index, "D").Value)
If Not rgFound Is Nothing Then
Row = rgFound.Row
Worksheets("Sheet1").Range("E" & Row).Value = Worksheets("Sheet2").Range("AA" & Index).Value
Worksheets("Sheet1").Range("F" & Row).Value = Worksheets("Sheet2").Range("AB" & Index).Value
Worksheets("Sheet1").Range("G" & Row).Value = Worksheets("Sheet2").Range("AC" & Index).Value
Worksheets("Sheet1").Range("H" & Row).Value = Worksheets("Sheet2").Range("Z" & Index).Value
Worksheets("Sheet1").Range("J" & Row).Value = Worksheets("Sheet2").Range("AG" & Index).Value
Worksheets("Sheet1").Range("I" & Row).Value = Worksheets("Sheet2").Range("AD" & Index).Value
Else
' Function // Not done yet
End If
Index = Index + 1
Loop
End Sub
Is the built in Find function effective? The loop loops trough roughly 250-400 values. Any Ideas?

Macro to group data by month

I'm trying to create a macro that will group data by month: for example, if there are 3 entries for February for client A, then it will consolidate the three entries into one and sum the amounts.
I have:
A: Client name
B: Invoice number
C: Billing month
D: Currency
E: Charge amount
F: Invoice step
What I am trying to do, is group the following into a single row, with the invoice amounts added up, and replace the three rows with just the one row. These entries are for a single client (so the grouping depends on the value in column A).
EG. Client A has three entries for Jan, Client B has one, Client C has one. Then for February Client A has one, Client B has one and Client C has two.
Any macro suggestions I've seen on here haven't been helpful to me, I keep getting errors coming up so I don't know what the problem is. This is the one I tried:
Sub Group()
Dim e As Range, a as Range
Set e = Range("C6")
Set c = e.Offset(, 2)
Do
If Evaluate("=month(" & e.Address & ")") <> Evaluate("=month(" & e(2).Address & ")") Then
e(2).Resize(2, 3).Insert
e(2).Offset(, 2) = "=sum(" & Range(a, c.Offset(, 2)).Address & ")"
e(2).Offset(, 2).Font.Bold = 1
Set e = e.End(4)
Set c = e.Offset(, 2)
Else
Set e = e(2)
End If
Loop Until e.End(4).Row = Rows.Count
e(2).Offset(, 2) = "=sum(" & Range(c, e.Offset(, 2)).Address & ")"
e(2).Offset(, 2).Font.Bold = 1
End Sub
EDIT: Subtotals and pivot tables would not work - the raw data is pasted in (thousands of lines of entry, for 40+ clients and some have many invoices each month), the raw data is sorted using a macro, which is then pasted into other sheets. Pasting from the pivot would be more difficult.
I follow your image to write the code:
After the code:
Here is the code:
Sub TEST()
Dim lastrow As Long
lastrow = Range("A" & Rows.Count).End(xlUp).Row
For i = 6 To lastrow
lastrow = Range("A" & Rows.Count).End(xlUp).Row
For j = i + 1 To lastrow
If Range("A" & j) = Range("A" & i) And Range("C" & j) = Range("C" & i) Then
Range("B" & i) = Range("B" & i) & "," & " " & Range("B" & j)
Range("E" & i) = Range("E" & i).Value + Range("E" & j).Value
Rows(j).EntireRow.Delete
End If
Next j
Next i
End Sub

How do I get all the different unique combinations of 3 columns using VBA in Excel?

I have an Excel worksheet with several columns, where 3 of them form a "unique key".
If I have fruits in column A (Apple, Banana, Orange), some name in column B (John, Peter) and something like Yes/No in column C, I want to be able to get sums of values from rows where the values in these columns are the same.
For instance, the sum of all the values in column D for rows where columns A, B and C are Apple,John,Yes.
Sorry for the confusing text, but I don't know how to express my question more clearly. I've never done anything in VBA so I'm a bit lost here...
Here's an example of the expected result.
It is also possible to go to the data tab -> remove duplicates. You can then select which columns you would like to compare in removing the dulicates.
I don't know in advance which combinations exist. The goal is to list all the unique combinations and the sum of some other columns. Can I do this without using VBA?
No Formulas/VBA required. Use a Pivot table for a summary of all combinations. See screenshot
If you still want VBA then that can also be done :)
EDIT
I quickly wrote this
Sub sample()
Dim ws As Worksheet
Dim lRow As Long, i As Long, j As Long
Dim col As New Collection
Dim Itm
Dim cField As String
Const deLim As String = "#"
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 2 To lRow
cField = .Range("A" & i).Value & deLim & _
.Range("B" & i).Value & deLim & _
.Range("C" & i).Value
On Error Resume Next
col.Add cField, CStr(cField)
On Error GoTo 0
Next i
i = 2
.Range("A1:C1").Copy .Range("F1")
.Range("I1").Value = "Count"
For Each Itm In col
.Range("F" & i).Value = Split(Itm, deLim)(0)
.Range("G" & i).Value = Split(Itm, deLim)(1)
.Range("H" & i).Value = Split(Itm, deLim)(2)
For j = 2 To lRow
cField = .Range("A" & j).Value & deLim & _
.Range("B" & j).Value & deLim & _
.Range("C" & j).Value
If Itm = cField Then nCount = nCount + 1
Next
.Range("I" & i).Value = nCount
i = i + 1
nCount = 0
Next Itm
End With
End Sub

Need it to search through duplicate records until finds criteria

I'm still quite new to VBA and struggling with the following code!
What I am trying to do is have the search function look through all cells within column 1 and find the criteria of Cell 1 matches PickerName2.Text and offset cell 5 is <>0 and then offset cell 6 = Blank
The problem I have is there a duplicates all the way down column 1 and as soon as it finds the matching name to PickerName2 Then it checks the offset cells 5 & 6 and as these don't match the criteria it gives the message they aren't currently picking.
This is even though further down the sheet there is a record that matches the criteria. I want it to look through all records until it find the criteria or if it has checked all populated cells in column A and nothing matches then it will give the message that they aren't currently picking.
I do hope someone can help :-)
Al
Private Sub CommandButton3_Click()
Dim iRow As Long
Dim ws As Worksheet
Dim strSearch As String
Dim aCell As Range
Dim rng As Range, i As Long
'~~> Set the sheet where you want to search the IMEI
Set ws = Sheets("PickData")
With ws
'~~> Get the value which you want to search
strSearch = PickerName2.Text
'~~> Column A is Column 1 so Column B is 2. This is where we are searching
'~~> xlWhole is used in the code below so that we find a complete match
'~~> xlPart is supposed to be used when you are finding a partial match.
Set aCell = .Columns(1).Find(What:=strSearch, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
'~~> get the row of the cell where we found the match and add data to
If Not aCell Is Nothing And aCell.Offset(0, 5).Value <> "" And aCell.Offset(0, 6).Value = "" Then
MsgBox " " & PickerName2.Text & " is currently picking - " & aCell.Offset(0, 1) & " " & aCell.Offset(0, 2) _
& " " & aCell.Offset(0, 3) _
& " "
UserForm1.Hide
Else
MsgBox " " & PickerName2.Text & " has no outstanding PIK!", vbExclamation
PickerName2.Value = ""
Exit Sub
End If
End With
PickNo2.Value = ""
PickerName2.Value = ""
UserForm1.Hide
End Sub
I have done some searching online and tried something similar to the following code but keep getting a object required error but cannot see where I am going wrong?
Dim rng As Range
Dim i As Integer
Dim finalrow As Integer
finalrow = Sheets("PickData").Range("A10000").End(xlUp).Row
For i = 2 To finalrow
If Cells(i, 1).Value = UserForm1.PickerName2.Text And (Cell.Offset(i, 5) <> "") And (Cell.Offset(i, 6) = "") Then
MsgBox " " & PickerName2.Text & " is currently picking - " & Cell.Offset(i, 1) & " " & Cell.Offset(i, 2) _
& " " & Cell.Offset(i, 3) _
& " "
End If
Next i
Your first code attempt using the Range.Find method is a good start.
Now extend this approach by using the Range.FindNext method.
This link should help you.
In your second block of code:
The references to Cell.Offset... should probably be something like Cells(i,1).Offset(0,5) and Cells(i,1).Offset(0,1) and Cells(i,1).Offset(0,3) respectively.
Also the second reference to PickerName2 should be qualified with UserForm1, as in UserForm1.PickerName2