I'm trying to create a single file by consolidating 4 excel files into 1.
here is the code for the same
Public Sub CommandButton1_Click()
Dim wb As Workbook
Dim row As Long
Dim i As Long
Dim currow As Long
Dim LastRow As Long
LastRow = Sheets("Sheet1").Cells(65536, 2).End(xlUp).row
Columns("O:O").Insert
Range("O1").Value = "Source"
For i = 1 To 4 Step 1
Sheets("Sheet1").Range("O2:O" & LastRow).Value = "Dump" & i
Set wb = Workbooks.Open("C:\Users\BV\Desktop\Excel" & "\Dump" & i & ".xlsx")
With wb.Sheets("Data")
If i = 1 Then
row = 1
Else
row = 2
End If
Do Until .Range("A" & row).Value = vbNullString
currow = currow + 1
For n = 0 To 13 Step 1
Me.Range("A" & currow).Offset(columnoffset:=n).Value = .Range("A" & row).Offset(columnoffset:=n).Value
Next n
row = row + 1
Loop
End With
wb.Close True
Next i
Fees_check
Set wb = Nothing
End Sub
Dump1,Dump2,Dump3 and Dump4 are the source file names.
I want to add another column which gives the source file name after consolidating like Dump1,Dump2 etc.
Right now im getting the value as Dump4 for all the rows.
Any help is appreciated.
Thanks
Your problem is this line:
Sheets("Sheet1").Range("O2:O" & LastRow).Value = "Dump" & i
Every time you go through the loop you just replace values from "O2" to the last row with dump & i which, on the final time, just fills the whole column with "dump4"
So, get rid of that line and use your CurrRow variable to populate column "O" instead, maybe by just inserting a line in the bit that actually moves across the data, like this:
For n = 0 To 13 Step 1
Me.Range("A" & currow).Offset(columnoffset:=n).Value = .Range("A" & row).Offset(columnoffset:=n).Value
Me.Range("O" & currow).Value2 = "Dump" & i ' INSERT THIS LINE
Next n
This is a quick fix, I'd normally fill a range in one go but I think you're just dealing with small amounts of data it should be fine.
I Have Table like this, where i have to use macro because my table always change Every day (SSAS)
so i have use macro to filter automatically,
I am able to sum Amount based on same Vendorname, PONuber and Date on Column E (Subtotal).
and then filter to show Subtotal AMount >500
I want to show only row >500 (Column E), and pop up message to count PONumber (Column B) how many Unique PO Number (Only Visible Row to count)
i've been stuck how to count only Visible Unique PO Number and show it on Pop Up message
this is my Macro
Sub FilterCOunt_Click()
Dim Condition As Variant
Dim AVal As Variant
Dim LastRow As Long
Dim Hide, popup As Long
Dim message As String
Dim sht As Worksheet
'----------------------------
Dim dictionary As Object
Set dictionary = CreateObject("scripting.dictionary")
'---------------------------
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.AskToUpdateLinks = False
Application.DisplayAlerts = False
Application.Calculation = xlAutomatic
Application.ScreenUpdating = False
Application.StatusBar = False
'------------------
Columns.EntireColumn.Hidden = False
Rows.EntireRow.Hidden = False
Columns("E:Z").EntireColumn.Delete
Range("E:Z").EntireColumn.Insert
Range("E1").Value = "Sub Total >500 "
Set sht = ActiveSheet
LastRow = sht.Range("B" & Rows.Count).End(xlUp).Row
'-------------------
For i = 2 To LastRow ' with last row count =SUMIFS(I:I,A:A,A8,B:B,B8,C:C,C8)
AVal = "A" & i
BVal = "B" & i
CVal = "C" & i
Worksheets("Sheet3").Range("E" & i).Formula = "=SUMIFS(D:D,A:A," & AVal & ",B:B," & BVal & ",C:C," & CVal & ")"
Next i
With sht.Range("E1:E" & LastRow)
.AutoFilter
.AutoFilter field:=1, Criteria1:=">=500"
End With
'----------Count Pop UP
Dim CountPO As Long
Range("G1").FormulaArray = "=SUM(IF(FREQUENCY(IF(SUBTOTAL(3,OFFSET(B2,ROW(B2:B22)-ROW(B2),1)),IF(B2:B22<>"",MATCH(""&B2:B22,B2:B22&"",0))),ROW(B2:B22)- ROW(B2)+1),1))"
MsgBox "We Found " & CountPO & " PO Open(s)", _
vbInformation, "PO Found"
End Sub
and this is the formula to count it
{=SUM(IF(FREQUENCY(IF(SUBTOTAL(3,OFFSET(B2,ROW(B2:B22)-ROW(B2),1)),IF(B2:B22<>"",MATCH("~"&B2:B22,B2:B22&"",0))),ROW(B2:B22)-ROW(B2)+1),1))}
If you are pulling from a Database via SSAS you can use Power Query to link to your SSAS DataModel to Excel and you can insert a Calculated Measure in Dax from there with DistinctCount.
Count:=Calculate(DistinctCount(TableName[PONumber]),TableName[Amount]>500)
Alternatively if you want total insights on your specified issue you can add a measured column and then you can use Power Pivot to filter for your criteria live on refresh to the data model, completely negating the need for VBA entirely.
Incidentally it is pertinent to remember VBA is the sledge hammer of solutions please use the DataModel Tools before you ever think of a macro solution remember, VBA is an Application Programming Language and many IT Security Systems will disable it because it opens the system up for malware, you can literally change any file or program in VBA including calling delete system files
Meanwhile having a set DataModel in a locked file that requires user access behind LAN security is easily more secure than allowing your computer to have open programatic access.
This is an alternative formula (which doesn't require any filtering)
=SUM(--(FREQUENCY(IF(B2:B20>500,MATCH(A2:A20,A2:A20,0)),ROW(A2:A20)-ROW(A2)+1)>0))
It's an array formula so using VBA
Range("E1").FormulaArray = "=SUM(--(FREQUENCY(IF(B2:B20>500,MATCH(A2:A20,A2:A20,0)),ROW(A2:A20)-ROW(A2)+1)>0))"
A formula for your cell E2, which is not an array formula, is
=SUMPRODUCT((B2=B$2:B$23)*(A2=A$2:A$23)*(D$2:D$23))
Copy it down, as usual.
See here for why not using an array formula (if you have an alternative).
I am not certain this solves your question, as I did not fully understand it.
You can use the following code. I have implemented Collection to get the unique count.
This will count the unique rows in B column where value in E column > 500.
Private Sub GetUniqueCount() AS Variant
Dim Test As New Collection
Dim rng As Range
For i = 2 To 6 'Replace 6 with last row(without filtration)
Value = Cells(i, "B").Value
check = Contains(Test, Value)
If Cells(i, "E").Value > 500 And Not check And Len(Value) > 0 Then
Test.Add Value, CStr(Value)
End If
Next i
GetUniqueCount = Test.count
End Sub
'Function to check if the value exists in Collection or not
Public Function Contains(col As Collection, key As Variant) As Boolean
Dim obj As Variant
On Error GoTo err
Contains = True
obj = col(key)
Exit Function
err:
Contains = False
End Function
Step 1: Post my code to a new module.
Step 2: Bind you button to the macro named "filterAndCount"
Step 3: Click the buton and rejoice :-)
Code description:
1) The code loops all the rows in the table.
2) First it checks if the Sub Total is above the limit (500).
3) If equal or below it hides the row and moves on to the next row.
4) If above it checks if the value already exists in the array values above.
5) If it does not exists then the value is added to the array.
6) When all rows have been looped only rows with a Sub Total above the limit is visible.
7) Only the unique and visible PO Numbers have been added to the array.
8) The number of values in the array is dispayed in a message box.
Dim wb As Workbook
Dim ws As Worksheet
Dim i As Double
Dim n As Double
Dim subTotalLimit As Double
Dim arr() As String
Sub filterAndCount()
Set wb = ThisWorkbook
Set ws = wb.ActiveSheet
i = 2
subTotalLimit = 500
n = 0
ReDim arr(0 To 0) As String
arr(0) = 0
ws.Columns("E:Z").EntireColumn.Delete
ws.Range("E:Z").EntireColumn.Insert
ws.Range("E1").Value = "Sub Total >500 "
Do While ws.Range("B" & i) <> ""
ws.Range("E" & i).Formula = "=SUMIFS(D:D,A:A,A" & i & ",B:B,B" & i & ",C:C,C" & i & ")"
If ws.Range("E" & i) < subTotalLimit Then
ws.Range("B" & i).EntireRow.Hidden = True
Else
If Not IsNumeric(Application.Match(Range("B" & i).Text, arr(), 0)) Then
arr(n) = Range("B" & i).Value
n = UBound(arr) + 1
ReDim Preserve arr(0 To n) As String
arr(n) = 0
End If
End If
i = i + 1
Loop
MsgBox UBound(arr)
End Sub
Use 2 Dictionary Objects, one for totals and one for unique PO's
Sub filterCOunt()
Const LIMIT = 500
Dim wb As Workbook, ws As Worksheet
Dim iRow As Long, iLastRow As Long, amount As Single
Dim sVendor As String, sPO As String, msg As String, sKey As String
Dim dictPO As Object, dictTotal As Object
Set dictPO = CreateObject("Scripting.Dictionary")
Set dictTotal = CreateObject("Scripting.Dictionary")
Set wb = ThisWorkbook
Set ws = ActiveSheet
iLastRow = ws.Range("B" & Rows.Count).End(xlUp).Row
' first pass to total by po and vendor
For iRow = 2 To iLastRow
sVendor = Trim(ws.Cells(iRow, 1))
sPO = Trim(ws.Cells(iRow, 2))
amount = CSng(ws.Cells(iRow, 4))
sKey = sVendor & "_" & sPO
' sub total
If dictTotal.exists(sKey) Then
dictTotal(sKey) = dictTotal(sKey) + amount
Else
dictTotal.Add sKey, amount
End If
Next
' second pass for PO numbers
For iRow = 2 To iLastRow
sVendor = Trim(ws.Cells(iRow, 1))
sPO = Trim(ws.Cells(iRow, 2))
sKey = sVendor & "_" & sPO
' sub total
ws.Cells(iRow, 5) = dictTotal(sKey)
If dictTotal(sKey) > LIMIT Then
If Not dictPO.exists(sPO) Then
dictPO.Add sPO, iRow
End If
End If
Next
' filter
With ws.Range("E1:E" & iLastRow)
.AutoFilter
.AutoFilter field:=1, Criteria1:=">=" & LIMIT
End With
msg = "No of open PO's = " & dictPO.Count
MsgBox msg, vbInformation
End Sub
First, for your code Count Pop UP to work, let's change all from "" to """"
Second, to be able to notify a Unique PO Number and show it on Pop Up message, you must call the value received from cell G1, or, safer, use evaluate to get the result of this expression.
Your code will probably work now
'Dim CountPO As Long
Range("G1").FormulaArray = "=SUM(IF(FREQUENCY(IF(SUBTOTAL(3,OFFSET(B2,ROW(B2:B22)-ROW(B2),1)),IF(B2:B22<>"""",MATCH(""""&B2:B22,B2:B22&"""",0))),ROW(B2:B22)- ROW(B2)+1),1))"
MsgBox "We Found " & [g1].Value2 & " PO Open(s)", vbInformation, "PO Found"
however, your formula only counts all unique values including less than 500, in addition it is quite long. You can replace it using the shorter formula like the following code:
Dim formula_string As String
formula_string = "=SUMPRODUCT((B2:B22>3)*(C2:C22<>"""")/COUNTIF(B2:B22,B2:B22&""""))"
MsgBox "We Found " & Application.Evaluate(formula_string) & " PO Open(s)", vbInformation, "PO Found"
Hope it helps!
I am working with cells in a column, which have to be split. Element 1 of the string is supposed to be posted separately from Element 2 of the same string, each on another Worksheet.
String "123 ABC" -> "123" in column C and "ABC" in column D
I am running into a Runtime-Error 9 "Index out of Range" if one of the cells I am checking only contains "123" or "ABC" but no both parts.
I tried to work around it in the way you see in my code below. Needless to say it does not work.
Could one of the more experienced Excel-Gurus help me out here?
Thank you in advance for your time!
Application.ScreenUpdating = False
Dim wbInput As Workbook, wbOutput As Workbook
Set wbOutput = ActiveWorkbook
Dim wsInput As Worksheet, wsOutput As Worksheet, wsMistakes As Worksheet
Set wsOutput = wbOutput.Worksheets("FehlerVorkommen")
Set wsMistakes = wbOutput.Worksheets("NichtZuweisbar")
Dim lRowInput As Long, lRowOutput As Long, lRowMistakes As Long
Dim Lieferant As Range
Dim InputFile As String, myElements() As String
lRowOutput = wsOutput.Range("A" & Rows.Count).End(xlUp).Row
wsOutput.Range("A2:G" & lRowOutput).Clear
wsMistakes.Range("A2:G500").Clear
InputFile = Application.GetOpenFilename()
If InputFile = "Falsch" Then
Exit Sub
End If
Set wbInput = Workbooks.Open(InputFile)
Set wsInput = wbInput.Worksheets("owssvr")
lRowInput = wsInput.Range("A" & Rows.Count).End(xlUp).Row
'Get all Information
For Each Lieferant In wsInput.Columns(1).Rows("2:" & lRowInput)
If wsInput.Columns(3).Rows(Lieferant.Row) <> vbNullString Then
myElements = Split(wsInput.Columns(3).Rows(Lieferant.Row).Value, " ", 2) 'A maximum of 2 String-Parts to avoid 4-5 splits whenever there is a GmbH or AG or whatever
If IsEmpty(myElements(1)) = True Then <<<<<<<<<ERROR HERE<<<<<<<<<<<
lRowMistakes = wsMistakes.Range("A" & Rows.Count).End(xlUp).Row
NextRow = lRowMistakes + 1
wsInput.Columns(1).Rows(Lieferant.Row).Copy Destination:=wsMistakes.Columns(1).Rows(NextRow)
NextRow = NextRow + 1
Else
If IsNumeric(wsInput.Columns(1).Rows(Lieferant.Row)) = True And wsInput.Columns(1).Rows(Lieferant.Row) <> vbNullString _
And IsNumeric(wsInput.Columns(2).Rows(Lieferant.Row)) = True And wsInput.Columns(2).Rows(Lieferant.Row) <> vbNullString Then
wsInput.Columns(1).Rows("2:" & lRowInput).Copy Destination:=wsOutput.Columns(1).Rows("2:" & lRowInput) 'Task Namen
wsInput.Columns(2).Rows("2:" & lRowInput).Copy Destination:=wsOutput.Columns(2).Rows("2:" & lRowInput) 'Bestellpositionen
wsOutput.Columns(3).Rows(Lieferant.Row).Value = myElements(0) 'ID
wsOutput.Columns(4).Rows(Lieferant.Row).Value = myElements(1) 'Name
wsInput.Columns(3).Rows("2:" & lRowInput).Copy Destination:=wsOutput.Columns(5).Rows("2:" & lRowInput) 'Fehlerarten
Else 'Get all wrong inputs on separate Sheet
lRowMistakes = wsMistakes.Range("A" & Rows.Count).End(xlUp).Row
NextRow = lRowMistakes + 1
wsInput.Columns(1).Rows(Lieferant.Row).Copy Destination:=wsMistakes.Columns(1).Rows(NextRow)
NextRow = NextRow + 1
End If
End If
Else 'Get all wrong input on separate Sheet
lRowMistakes = wsMistakes.Range("A" & Rows.Count).End(xlUp).Row
NextRow = lRowMistakes + 1
wsInput.Columns(1).Rows(Lieferant.Row).Copy Destination:=wsMistakes.Columns(1).Rows(NextRow)
NextRow = NextRow + 1
End If
Next Lieferant
wbInput.Close
This line doesn't do what you think it's doing:
If IsEmpty(myElements(1)) = True
First, specifying a limit for the Split function doesn't mean that you always get that many elements in the array. Second, IsEmpty tests to see if a Variant is type VT_EMPTY, not whether a String has a value (Split returns a strongly typed array).
Just test the UBound instead:
If UBound(myElements) > 0 Then
if you see below, I have 3 columns, all i basically want is to check column A and Column B, if the carrier (Column A) and Date (Column B) are equal then it will have the same Order Number.
For example: In this case, A3 = A6 and B3 = B6 , so it should have the same order number as one above (160) not 163. I hope this makes it clear.
Thanks for the help. I appreciate it :)
This was quite interesting, so I went ahead and wrote some code. Copy this into a new module and change the sheetname etc. to fit to your workbook. You may also need to redefine fr (firstrow, currently set to 2). The code also currently marks all the changed order-numbers red with the line .Range("C" & r).Font.ColorIndex = 3. Delete / comment it, if you don't want that.
Sub matching()
Dim wb As Workbook
Dim tws As Worksheet
Dim keys() As String
Dim tmpKey As String
Dim pos As Integer
Dim fr, lr As Integer 'first row, last row of data
Set wb = ThisWorkbook
Set tws = wb.Worksheets("Vigmo")
fr = 2
lr = tws.Range("A1000000").End(xlUp).Row
ReDim keys(1 To lr - 1)
With tws
keys(1) = .Range("A" & fr).Value & "_" & .Range("B" & fr).Value
End With
For r = fr + 1 To lr
With tws
tmpKey = .Range("A" & r).Value & "_" & .Range("B" & r).Value
If UBound(Filter(keys, tmpKey)) >= 0 And tmpKey <> "_" Then
'found in array -> replace orderNumber
'On Error resume next
pos = Application.Match(tmpKey, keys, 0)
'On Error goto 0
.Range("C" & r).Value = .Range("C" & pos + 1).Value
.Range("C" & r).Font.ColorIndex = 3
Else
'not found -> next
End If
keys(r - 1) = tmpKey
End With
Next r
End Sub
Let me know if you have any questions as to how this code works!
Below is some code that I came up with that does what your looking for. I dont know how you are generating your order numbers but I assumed they are already present. Hope this helps you :)
Sub OrderNumber()
Dim SearchTerm As String
Dim DateTerm As Date
Dim NumberOfEntries As Long
Dim wks As Excel.Worksheet
Set wks = Worksheets("Sheet1") '<==== Sets the workbook. change it to what yours is called
NumberOfEntries = Application.WorksheetFunction.CountA(wks.Range("A:A")) '<=== Find the number of entries
For x = 2 To NumberOfEntries '<==== Goes through all the entries
SearchTerm = wks.Cells(x, 1) '<===== The Search term (Carrier)
DateTerm = CDate(wks.Cells(x, 2)) '<==== The search Date
For y = x To NumberOfEntries '<===== goes through everything below the search term to speed things up
If wks.Cells(y, 1) = SearchTerm And CDate(wks.Cells(y, 2)) = DateTerm Then '<=== If the name and the date match then
wks.Cells(y, 3) = wks.Cells(x, 3) '<==== Copy the order number
End If
Next y
Next x
End Sub
Just put this in a module or wherever you want but I made it in a module.
G
I'm building an master excel file that is designed to gather data from lots of other excel files that are stored in the business Dropbox files and place them in the 2nd sheet of the master file. I built a original version on my local computer and that worked perfectly (the path3 variable) but once I tried to convert it based on a changing file path (because each user will have a different path from their PC) I am getting the run time error. The formula defined by path2 is what I have been trying to use but even though the variable seems to be holding the right value (I tested it by having it write out the values) it doesn't seem to be able to move the data, throwing the above error and highlighting the "rngdest.Formula = Chr(61) & path2" line. I really don't have any idea what is causing this and I have spent several days trying different approaches but to no avail so any ideas, solutions or links to already solved (I have spent a long time searching but haven't found anything) would be very much appreciated.
I've included the whole of the code for completeness, I think I've removed most of the redundant code that I left in but there may be some still left. If you need any clarifications on the code please let me know. Thanks for any potential help
Private Sub CommandButton2_Click()
Dim counter As Integer
Dim i As Long
Dim j As Long
Dim k As Long
Dim l As Long
Dim a As Integer
Dim z As Integer
Dim y As Integer
Dim p As Integer
Dim Names() As String
Dim Fix1() As String
Dim path3 As String
Dim path2 As String
Dim SheetName As String
Dim c As Range
Dim found As Range
Dim BookName As String
Dim var1 As String
Dim rngdest As Range
Dim rngsource As Range
Dim cell As String
Dim adjust As Integer
Dim adjust2 As Integer
Dim rngname As Range
Dim colNo As Integer
Dim fin As String
Dim fin2 As String
Dim fin3 As String
Dim comp As String
Dim teststring As String
Dim currentWb2 As Workbook
Set currentWb2 = ThisWorkbook
MsgBox "Excel will now update the sheet, please be patient as this can take a few minutes. You will be notified once it is complete"
ReDim Fix1(1 To 4)
Fix1(1) = "A-F"
Fix1(2) = "G-L"
Fix1(3) = "M-R"
Fix1(4) = "S-Z"
counter = 0
With ActiveSheet
i = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
ReDim Names(1 To i, 1 To 4)
With ActiveSheet
For k = 1 To 4
For a = 2 To i
Names(a, k) = Cells(a, k).Value
Next a
Next k
End With
SheetName = "Analysis"
BookName = "Outcomes Final.xlsm"
For p = 1 To 4
fin2 = Split(Cells(, p).Address, "$")(1)
With ActiveSheet
l = .Cells(.Rows.Count, fin2).End(xlUp).Row
End With
For z = 1 To l
counter = counter + 1
fin = Split(Cells(, counter).Address, "$")(1)
currentWb2.Sheets("Sheet2").Range("" & fin & "1") = Names(z, p)
For y = 1 To 34
adjust = y + 1
cell = "$B$" & y & ""
If z = 1 Then
Else
teststring = GetPath()
teststring = teststring & "\Clients\"
path3 = "'C:\Users\Lewis\Documents\Outcomes\Floating Support\Clients\" & Fix1(p) & "\" & Names(z, p) & "\[Outcomes Final.xlsm]Analysis'!" & cell & ""
path2 = teststring & Fix1(p) & "\" & Names(z, p) & "\Outcomes\[Outcomes Final.xlsm]Analysis'!" & cell & ""
End If
Set rngdest = currentWb2.Sheets("Sheet2").Range("" & fin & "" & adjust & "")
Set rngsource = Range("B" & y & "")
rngdest.Formula = Chr(61) & path2
Next y
Next z
Next p
currentWb2.Sheets("Sheet2").Columns(1).EntireColumn.Delete
currentWb2.Sheets("Sheet1").Range("A1:D35").Interior.ColorIndex = 0
For j = 1 To counter
fin3 = Split(Cells(, j).Address, "$")(1)
If currentWb2.Sheets("Sheet2").Range("" & fin3 & "35") = "1" Then
With currentWb2.Sheets("Sheet1").Range("A1:D35")
comp = currentWb2.Sheets("Sheet2").Range("" & fin3 & "1")
Set c = .Find(comp, LookIn:=xlValues)
If Not c Is Nothing Then
c.Interior.ColorIndex = 3
End If
End With
End If
Next j
MsgBox "The update is now complete, please click on sheet 2 to view the data. All clients in red have not been properly completed"
End Sub