VBA - lookup in variable workbook - excel

I have a little bit of a problem within an Excel Macro
I have a file populated with huge amount of data extracted from different software and that has to be updated on a monthly basis.
There is data from A2 to O & Lastrow
in column L there is a code that I use to split the sheet into as many sheets as needed (one per existing codes)
Lets say my sheet has 50 lines, the codes present in L are 0250, 40 times and 1225, 10 times so I'll have 2 new sheets
Then all these sheets are saved as individual files for further treatment - and column N of the newly created file will be changed by human
So excel will create a folder and save these 2 sheets as 2 xlsx files according to the month we're in
In my example, it will save them as:
c:\ACCOUNTS\SEP\0250 SEP 19.xlsx
c:\ACCOUNTS\SEP\1225 SEP 19.xlsx
I'm looking for either in the raw extract entirely or at the time the file is split to add in column O a vlookup that will analyze each of these files from the previous month
So I'd like to have a piece of code that will input a Vlookup in the cells, depending of what's in column L of the sheet name to have
=vlookup(A2,c:\ACCOUNTS\AUG\0250 AUG 19.xlsx!A2:O & lastrow,14,0)
or
=vlookup(A2,c:\ACCOUNTS\AUG\1225 AUG 19.xlsx!A2:O & lastrow,14,0)
Sub SPLIT()
Dim main As Worksheet
Dim lastrow As Long
Dim FileExtStr As String
Dim FileFormatNum As Long
Dim xWs As Worksheet
Dim xWb As Workbook
Dim FolderName As String
Set xWb = Application.ThisWorkbook
mBefore = Format(DateAdd("m", -1, Date), "MMM yy")
mPrevious = Format(DateAdd("m", -1, Date), "MMM yy")
Set main = Sheets("Global")
main.Activate
'ActiveSheet.name = "Global"
'Set main = Sheets("Global")
'Calculation of last line
lastrow = main.Cells(Rows.Count, "l").End(xlUp).Row
'MsgBox lastrow
Set FDWDownload1 = Sheets("Global")
Sheets.Add(before:=Sheets("Global")).name = "List"
Set Ref = Sheets("List")
'Creates the table
FDWDownload1.Range("L" & StartPointRow + 1 & ":L" & lastrow).Copy
Ref.Activate
Ref.Range("B2").PasteSpecial Paste:=xlPasteValuesAndNumberFormats
Range("B:B").RemoveDuplicates Columns:=1
Application.CutCopyMode = False
RefLastRow = Ref.Cells(Rows.Count, "B").End(xlUp).Row
FDWDownload1.Activate
If RefLastRow > 0 Then
'For the second row to the end of the table on the References tab
For i = 3 To RefLastRow
'Copy the header from the download
FDWDownload1.Range("A1:P1").Copy
'Add a new sheet placed after the sheet "Global" & name it according to the reference table
Sheets.Add(after:=Sheets("Global")).name = Ref.Range("B" & i)
Set ws = Application.ActiveSheet
'Paste the header into each newly created sheet
ws.Range("A1").PasteSpecial Paste:=xlPasteValuesAndNumberFormats
ws.Range("A1").PasteSpecial Paste:=xlPasteFormats
'Get the number of the last row on the newly created sheet
ForLastRow = ws.Cells(Rows.Count, "L").End(xlUp).Row
'As the download is grouped in Product Line order find the first and last row of the Product according to the worksheet name
With FDWDownload1
FDWDownload1.Activate
k = .Range("L:L").Find(what:=ws.name, after:=.Range("L1")).Row
l = .Range("L:L").Find(what:=ws.name, after:=.Range("L1"), searchdirection:=xlPrevious).Row
End With
Range("A" & k & ":O" & l).Copy
ws.Activate
ws.Range("A" & ForLastRow + 1).PasteSpecial Paste:=xlPasteValuesAndNumberFormats
Columns("A:P").EntireColumn.AutoFit
Columns("A:O").AutoFilter
Range("A:O").Sort Key1:=Range("C1"), Order1:=xlAscending, Header:=xlYes
ActiveSheet.name = ws.name & " " & "GRIR" & " " & mBefore
Next i
Else
End If
End Sub

Has the solution to be in VBA? If not you should definetly check out PowerQuery which is included in Excel 2016 and higher. Power Query can do exactly what you asked and doesn't need any code.
Anyway, if I understand you correctly you need to set a formula with VBA.
Try this:
Sub set_formula()
lastrow = 10
Path = "C:\Folder\"
workbookname = "Template.xlsx"
sheetname = "Ids"
Range("D13").Formula = "=VLOOKUP(A2,'" & Path & "[" & workbookname & "]" & sheetname & "'!A2:O" & lastrow & ",14,0)"
End Sub

Related

Why do i fail to read .csv data properly and how could i write the code just to select specific columns?

the code reads all csv data files from the same path/folder in which i save this VBA Code in a seperate .xlsm file.
The first file gets formated pretty well so i have 3 clean columns. By starting to read the second file within the path it starts to read all data as .csv
Do You know why?
And how would it be possible just to read specific columns from the files which are not specifically next to each other? Like Column A and Column C for example.
Thank you so much in advance
Sub Zusammenfuegen()
Dim strOrdner As String
Dim lngZeile As Long, lngZeileMax As Long
Dim lngZMax As Long, lngZeileFrei As Long
Dim wkbQuelle As Workbook
Dim objLST As ListObject
Application.ScreenUpdating = False
Tabelle4.UsedRange.Clear
'Tabelle4.Range("A1:C1").Value = Array("Datum", "Umsatz", "Region")
lngZeileFrei = 1
strOrdner = ThisWorkbook.Path
With Tabelle1
lngZeileMax = .Cells(.Rows.Count, 1).End(xlUp).Row
For lngZeile = 2 To lngZeileMax
Workbooks.OpenText strOrdner & "\" & .Cells(lngZeile, 1).Value, Semicolon:=True, local:=True
Set wkbQuelle = ActiveWorkbook
.Cells(lngZeile, 2).Value = "eingelesen am " & Now
With wkbQuelle.Worksheets(1)
lngZMax = .Cells(.Rows.Count, 1).End(xlUp).Row
lngZeileFrei = Tabelle4.Cells(Tabelle4.Rows.Count, 1).End(xlUp).Row + 1
.Range(.Cells(2, 1), .Cells(lngZMax, 3)).Copy _
Destination:=Tabelle4.Cells(lngZeileFrei, 1)
End With
wkbQuelle.Close savechanges:=False
Next lngZeile
End With
With Tabelle4
Set objLST = .ListObjects.Add(SourceType:=xlSrcRange, _
Source:=.Range("A1").CurrentRegion, _
xlListObjectHasHeaders:=xlYes)
objLST.Name = "MeinListObject"
End With
Application.ScreenUpdating = True
End Sub
The code looks good and if there is any problem, check if csv is well-formatted.
With regard to specific columns: instead of taking all data
'.Range(.Cells(2, 1), .Cells(lngZMax, 3)).Copy _
'Destination:=Tabelle4.Cells(lngZeileFrei, 1)
set required range with named columns as below
.Range("A2:A" & lngZMax & ",C2:C" & lngZMax & ",D2:D" & lngZMax).Copy _
Destination:=Tabelle4.Cells(lngZeileFrei, 1)
For example, if source csv has 4 columns A B C D with 20 rows, where first row and second column B should be skipped out, then VBA function Range should look as
Range("A2:A20,C2:D20") or Range("A2:A20,C2:C20,D2:D20")

VBA saved workbooks do not open to the last active sheet

I am generating and saving multiple workbooks from another workbook, but when I reopen the saved workbooks the last activated sheet ("Summary") is not active.
I've tried different file formats with the same result. If I pause the code, the Activate command works and if I manually save it from there the Summary sheet is active when opened again. I do not want to save the generated workbooks as macro enabled with an Activate on open.
Sub brandSalesReports()
Set dataWb = ActiveWorkbook
wbPath = Application.ActiveWorkbook.Path
Set macroWb = Workbooks("Macros.xlsm")
macroWb.Activate
Set brandTable = ActiveSheet.ListObjects("BrandTable")
With brandTable.DataBodyRange
tRows = .Rows.Count
End With
dataWb.Activate
ActiveSheet.Name = "Original"
Sheets("Original").Copy Before:=Worksheets("Original")
ActiveSheet.Name = "Data"
LastRow = ActiveSheet.Cells(Rows.Count, 29).End(xlUp).Row 'Get Last Row
Range("AC1").Value = "Brand"
'Fill brand based on Product value
For i = 2 To LastRow
Product = Range("AF" & i).Value
For j = 2 To tRows + 1
Brand = brandTable.Range.Cells(j, 1).Value
If Product Like "*" & Brand & "*" Then
Range("AC" & i).Value = Brand
Exit For
End If
Next j
Next i
LastCol = ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Column
LastColLetter = Split(Cells(1, LastCol).Address, "$")(1)
'Create workbooks for each brand
For k = 2 To tRows + 1
Brand = brandTable.Range.Cells(k, 1).Value
For l = 1 To LastRow
If l = 1 Then
Set currentWb = Workbooks.Add(xlWBATWorksheet).Worksheets(1)
ActiveSheet.Name = "Data"
Set SourceRange = dataWb.Worksheets(1).Range("A1:" & LastColLetter & "1")
Set DestRange = currentWb.Range("A1:" & LastColLetter & "1")
DestRange.Value = SourceRange.Value
Row = 2
dataWb.Activate
ElseIf Range("B" & l).Value = Brand Then
Set SourceRange = dataWb.Worksheets(1).Range("A" & l & ":" & LastColLetter & l)
Set DestRange = currentWb.Range("A" & Row & ":" & LastColLetter & Row)
DestRange.Value = SourceRange.Value
Row = Row + 1
End If
Next l
currentWb.Activate
Worksheets.Add(Before:=Worksheets("Data")).Name = "Summary" 'Add new sheet
LastPivotRow = DSheet.Cells(Rows.Count, 1).End(xlUp).Row
If LastPivotRow <> 1 Then
'PIVOT TABLE CODE HERE, ALL WORKS
Sheets("Summary").Activate
Name = Brand & " Sales Report - " & Format(Date, "mm-dd-yy")
Application.DisplayAlerts = False
currentWb.SaveAs Filename:=wbPath & "\" & Name, FileFormat:=xlOpenXMLWorkbook
End If
ActiveWorkbook.Close
Next k
dataWb.Activate
End Sub
First and foremost, you should try to break your code apart into more well-defined blocks if you can.
Having a sub dedicated to building brand labels, a sub dedicated to creating new workbooks, a sub for writing the pivot tables, one for saving each workbook, and so on will make it easier to debug your code.
I specifically suggest this because breaking up your code into smaller subroutines can let you test less code at once. Therefore you will be able to determine if the issue in your code lies within a specific block, or how those blocks are being chained together (implementation bug vs. integration bug). If you know which block, (or combination of blocks) are causing your issue you can approach debugging in a more targeted manner.
Anyways, looking at your code you are using lots of implicit references. (ex: Range("AC1").Value = "Brand") I would instead urge you to use explicit references (ex: dataWb.Sheets("data").Range("AC1").Value = "Brand"). This is because implicit references are chosen by the computer at runtime, and are not always what you expect them to be. By explicitly stating to use "X" workbook's sheet "Y" we avoid referencing the wrong workbook/worksheet/table/etc.
As an argument to use explicit references, try this minimal example, which correctly saves a file with a summary sheet, and additionally correctly opens to the "summary" sheet.
Sub Test()
Dim wb As Workbook
Set wb = Application.Workbooks.Add()
wb.Activate
wb.Worksheets.Add(Before:=wb.Worksheets("Sheet1")).Name = "Summary"
wb.Sheets("Summary").Activate
wb.SaveAs "Test"
wb.Close (False)
End Sub
If changing your references to be explicit does not solve your issue, I suspect this might be something with your application state. Have you frozen the application before running macros or done anything else?

Copy row of data based on criteria AND "label" that copied data in last column

I have working code that checks for a criteria in each row, and if met, copies that whole row of data over to a different workbook. But! I need to be able to add text to the last column of the copied data (Column S) that essentially labels what criteria was met that made the code copy it over because I will soon be expanding to check for multiple different criteria.
So for every row that meets the criteria and gets copied, I want to add "Criteria1" next to it in column S in the new workbook (it will always be column S that will be the first available column).
I have mangled this code together through inheritance and all of your help, so I don't really even know where to begin.
Private Sub Workbook_AfterSave(ByVal Success As Boolean)
Dim CoderBook As Workbook
Dim Referrals As Worksheet
Dim Review As Workbook
Dim Crit As Worksheet
Dim LastRow As Long
Dim NextRow As Long
Dim i As Long
Set CoderBook = Workbooks.Open("Coder Referrals.xlsx")
Set Referrals = CoderBook.Sheets("Sheet1")
Set Review = ThisWorkbook
Set Crit = Review.Sheets("Criteria")
'Search code
LastRow = Crit.Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Application.ScreenUpdating = False
'Loop search code
For i = 2 To LastRow
'Specialized Criteria1 Check
If Crit.Range("F" & i) <> Crit.Range("G" & i) Or _
Crit.Range("I" & i) <> Crit.Range("J" & i) Then
'If meets Criteria1 check, then copy appropriate rows to CoderBook Referrals sheet
Referrals.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).EntireRow.Value = Crit.Rows(i).Value
End If
Next i
'End loop code
CoderBook.Close SaveChanges:=True
Application.ScreenUpdating = True
End Sub
Split the or into two statements:
For i = 2 To LastRow
j = Referrals.Cells(Rows.Count, 1).End(xlUp).row + 1
'Specialized Criteria1 Check
If Crit.Range("F" & i) <> Crit.Range("G" & i) Then
'If meets Criteria1 check, then copy appropriate rows to CoderBook Referrals sheet
Referrals.Rows(j).EntireRow.Value = Crit.Rows(i).Value
Referrals.Range("S" & j).Value = "Criteria1"
End If
If Crit.Range("I" & i) <> Crit.Range("J" & i) Then
Referrals.Rows(j).EntireRow.Value = Crit.Rows(i).Value
if Referrals.Range("S" & j).value = vbNullString then
Referrals.Range("S" & j).Value = "Criteria2"
Else
Referrals.Range("S" & j).Value = Referrals.Range("S" & j).Value & ", " & "Criteria2"
End if
Next i

Excel: comparing names in two different sheets

I have two excel sheets with about 500 rows each. Worksheet A has a name column E) and worksheet B has two name columns for small and large names(H&I). I want to create a loop that goes through and compares these columns and should it find a match paste this match onto a new worksheet.
Further clarification:
On worksheet B the two name columns are akin to Cigna and Cigna Co for example so they are not always the same name repeated, and on Worksheet A the name may be Cigna, so they are not always exact though. But the name of Worksheet A must match 1 or both names on Worksheet B.
Something like this is an easy loop and record, then see if anything from the first sheet is on the second.
Not sure what your small and large names situation is, so I checked both columns
In you VBA IDE go to the tools menu and selecte references. Select "Microstoft ActiveX data objects 2.8 Library. This will be used for the recordset.
Private Sub CommandButton1_Click()
Dim rs As New ADODB.Recordset
Dim ws As Excel.Worksheet
Dim ws2 As Excel.Worksheet
Dim lRow As Long
Dim lRowOut As Long
Set ws = ActiveWorkbook.Sheets("Sheet1")
ws.Activate
Set ws2 = ActiveWorkbook.Sheets("Sheet3")
'Add fields to your recordset for storing data.
With rs
.Fields.Append "Row", adInteger
.Fields.Append "Name", adChar, 25
.Open
End With
'Loop through and record the name
lRow = 1
Do While lRow <= ws.UsedRange.Rows.count
rs.AddNew
rs.Fields("Row").Value = lRow
rs.Fields("Name").Value = ws.Range("E" & lRow).Value
rs.Update
lRow = lRow + 1
ws.Range("A" & lRow).Activate
Loop
If rs.EOF = False Then
rs.MoveFirst
End If
'Switch to the second worksheet
Set ws = Nothing
Set ws = ActiveWorkbook.Sheets("Sheet2")
ws.Activate
'Loop through and see if anything on this sheet was on the first sheet.
lRow = 1
lRowOut = 1
Do While lRow <= ws.UsedRange.Rows.count
'Check if the column H name was recorded from the first sheet
rs.Filter = ""
rs.Filter = "Name='" & ws.Range("H" & lRow).Value & "'"
If rs.RecordCount = 0 Then
rs.Filter = ""
rs.Filter = "Name='" & ws.Range("I" & lRow).Value & "'"
If rs.RecordCount > 0 Then
'It has a date, delete the current row
ws2.Range("A" & lRowOut).Value = rs.Fields("Name").Value
lRowOut = lRowOut + 1
End If
ElseIf rs.RecordCount > 0 Then
'It has a date, delete the current row
ws2.Range("A" & lRowOut).Value = rs.Fields("Name").Value
lRowOut = lRowOut + 1
End If
lRow = lRow + 1
ws.Range("A" & lRow).Activate
Loop
End Sub
If you want to look for a part of the name as you say in your comment, you can use a like. Change the filter lines to something like this.
rs.Filter = "Name LIKE '%" & ws.Range("I" & lRow).Value & "%'"
Doing this with formulas, instead of VBA, I would stick a formula in A1 of your new workbook like the following. Assuming that your "Worksheet A" is in Book1 on Sheet1 and your H and I columns from "Worksheet B" are Book2 on Sheet1:
=if(countif([Book2]Sheet1!H:I, [Book1]Sheet1!E1)>1, [Book1]Sheet1!A1, "")
This says "If, in my columns H and I in Sheet1 of Book2 there is at last one match on the name from Cell E1 in Sheet1 of Book1, then grab the name from Cell E1 on Sheet1 of Book1"
This will leave a good number of blanks, but at that point you can just filter or sort them out.
If the requirements are more complicated than that, like any match across any of three columns, then you can just add the results of multiple CountIf() formulas and test them for > 1, or do a single Countif() for each column and then union the results, sort/filter, and Bob's your uncle.
If this is going to be something you do often, then it may be worth investing in the VBA route as that will take the little bit of manual work out of it.

How to check range/column of cells based on month and copy them

So essentially, what I'm trying to do is this:
------A--------B-------C-------D------
1 Date Weight Misc ID*
2 2014-06-12 210 445556
3 2014-07-13 150 546456
4 2014-08-14 265 546456
5 2014-09-15 655 655654
6 2014-10-16 87 546656
7 2014-10-17 1552 545488
8 2014-11-18 225 546545
I have a button and I want it to run a macro that checks if the dates in Column A fall within the current month. I've tried using
Month(Date)
but it checks the entire date, not the month only.
If the month in the cell in colmumn A equals the current month, I want it to copy the entire row of information corresponding to that particular cell. For example: When the current month is november, I want it to copy A8+B8+C8+D8, then I will paste that information in a whole different workbook.
Keep in mind that I'm completely new to VBA, but this is what I've come up with so far:
Sub dat()
Dim rng As Range
Dim dat As Date
dat = Month(Date)
For Each rng In Range("A2:A100")
If rng.Value = dat Then
Range("???").Copy
Range("A1").PasteSpecial
End If
Next
End Sub
Nothing really happens. If I change it to dat=Date then it only works for this particular day, and it takes forever to run through 1000 cells.
I was thinking if I could use Cells(Rows.Count, "A").End(xlUp).Value = Month(Date) somehow. Is this even possible?
EDIT: To paste in a different workbook I used the following commands:
Dim wb As Workbook
Set wb = Workbooks.Open("C:\Users\....DOCUMENT.xlsm")
And then to paste:
wb.Sheets("Sheet1").Range("A" & NextDest & ":F" & NextDest).PasteSpecial
Just change the "Destination Sheet" to the name of the sheet you want to copy to.
Sub dat()
Dim LastRow As Long
Dim CurRow As Long
Dim NextDest As Long
Dim ws As Worksheet
Set ws = Sheets("SOURCE SHEET")
LastRow = ws.Range("A" & Rows.Count).End(xlUp).Row
For CurRow = 2 To LastRow
If IsDate(ws.Range("A" & CurRow).Value) = True Then
If Month(ws.Range("A" & CurRow).Value) = Month(Date) Then
ws.Range("A" & CurRow & ":D" & CurRow).Copy
NextDest = Sheets("DESTINATION SHEET").Range("A" & Rows.Count).End(xlUp).Row + 1
Sheets("DESTINATION SHEET").Range("A" & NextDest & ":D" & NextDest).PasteSpecial
Else
End If
Else
ws.Cells(CurRow, 1).Interior.Color = RGB(255,0,0)
End If
Next CurRow
End Sub
Edit: Your DESTINATION SHEET will now add rows after the last used row. Also, the code will check if the value is a date first and will highlight if not a date.
Change this
If rng.Value = dat Then
To
If month(rng.Value) = dat Then

Resources