I need to run Vlookups for data in 2 workbooks. I will open both of my workbooks when running the VBA codes, and I saved both workbooks as .xlsm so they are both macro enabled.
I have no problem using the Vlookup Excel function but I want to run it automatically using VBA code.
Here is the information,
I have 2 workbooks, Book3.xlsm and Book32.xlsm. Book3 is where I want my result to be, as shown in the second picture. The data range varies each month, so I need to loop through the end of the last row.
I have 3 columns in Book3 ID and Type and Result and 2 columns in Book32, ID and Result, and I want to do Vlookup using the ID column in Book3 and get the values in Result columns in Book32. The data are both in Sheet1.
Now my code will run but please look for the first picture where it is not showing the desired result. I can leave the value as #N/A if can't be found but in this case, all the values should be found using Vlookup.
Here is my code,
Sub test()
On Error Resume Next
Dim Res_Row As Integer
Dim Res_Clm As Integer
Dim Table1 As Range
Dim Table2 As Range
Dim cl As Range
Set Table1 = Workbooks("Book3.xlsm").Sheets("Sheet1").Columns("A:C")
Set Table2 = Workbooks("Book32.xlsm").Sheets("Sheet1").Columns("A:B")
Res_Row = Sheet1.Range("C2").Row
Res_Clm = Sheet1.Range("C2").Column
For Each cl In Table1
Sheet1.Cells(Res_Row, Res_Clm) = Application.WorksheetFunction.VLookup(cl, Table2, 2, False)
Res_Row = Res_Row + 1
Next cl
MsgBox "Done"
End Sub
How about this code, which avoids the loop and is easier to read \ maintain.
With Workbooks("Book3.xlsm").Sheets("Sheet1")
Dim lRow as Long
lRow = .Range("A" & .Rows.Count).End(xlup).Row
With .Range("C2:C" & lRow)
.FormulaR1C1 = "=Vlookup(RC[-2],[Book32.xlsm]Sheet1!C1:C2,2,0)"
.Value = .Value
End With
End With
Here it is :
Sub test()
On Error Resume Next
Dim Res_Row As Integer
Dim Res_Clm As Integer
Dim Table1 As Range
Dim Table2 As Range
Set Table1 = Workbooks("Book3.xlsm").Sheets("Sheet1").Columns("A:C")
Set Table2 = Workbooks("Book32.xlsm").Sheets("Sheet1").Columns("A:B")
Res_Clm = 3
The loop will be on each rows of the table1.
For Each i In Table1.Rows
If Sheet1.Cells(i.row, 1) = "" Then Exit For
If there is no data ("") in the cell, the program exit the loop
Sheet1.Cells(i.row, Res_Clm) = Application.WorksheetFunction.VLookup(Sheet1.Cells(i.row, 1), Table2, 2, False)
Next i
Next i increment the i of the for each loop.. It is like i = i+1
MsgBox "Done"
End Sub
You use the wrong argument for the first argument of the vlookup.
Also your loop on "cl" would only work on three rows, so I use row argument.
In general, I would adwise to code your vlookup fonction instead of using Application.WorksheetFunction.VLookup. I'm quite sure it is longer.
Related
I am trying to fetch the value using a vba macro and vlookup in excel. Expected value is the value from "hiveCatalog" sheet but actual result is "N/A"
When i try normal vlookup in excel, i do get the expected value.
Below is the code - written to update a specific column in the first sheet using the value from another sheet.
Sub **addVlookUpColumn**()
Dim Row As Long
Dim Clm As Long
Dim Sheet1 As Worksheet, Sheet2 As Worksheet
Set Sheet1 = ThisWorkbook.Worksheets("Attributesstagingdirectload")
Set Sheet2 = ThisWorkbook.Worksheets("hiveCatalog")
Table1 = Sheet1.Range("A2:A11000")
Table2 = Sheet2.Range("A3:A11000")
Row = Sheet1.Range("D2").Row
Clm = Sheet2.Range("D2").Column
For Each cl In Table1
Sheet1.Cells(Row, Clm) = Application.VLookup(c1, Table2, 1, False)
Row = Row + 1
Next cl
End Sub
Do let me know where i am going wrong
Below image contains the column header of both the sheets and sheet names
Maybe you need to set where table2 is
You can give this a try if hiveCatalog is where the table is located.
Sheet1.Cells(Row, Clm) = Application.VLookup(Sheet1.Cells(Row, Clm) = WorksheetFunction.VLookup(c1, Worksheets("hiveCatalog").Table2, 1, False))
I'm currently creating a worksheets which moves rows in Excel that contain a cell containing the text "Voltooid" to a second worksheet. I want to either move the rows, or copy+paste and delete them, based on cell value.
Up until now, I've been able to build a function that deletes rows in the table. Please see the code below:
Sub DeleteCompleted()
Dim Test1 As ListObject
Dim Test2 As Variant
Dim Rowcount As Integer
Set Test1 = Sheets("To Do Lijst").ListObjects("Table2")
Test2 = Test1.ListRows.Count
For Rowcount = 1 To Test2 Step 1
If Test1.DataBodyRange(Rowcount, 4) = "Voltooid" Then
Test1.DataBodyRange(Rowcount, 4).Delete Shift:=xlUp
Rowcount = 0
End If
Next Rowcount
Can you suggest a better format for me?
Kind regards,
Pim
Using descriptive variable names makes your life easier
Always use Long for row counting variables, Excel has more rows than Integer can handle. Also never use Variant unless you really have to.
If you delete/add rows you must loop backwards, because deleting/adding changes the row count.
To move a row use Range().Cut and define a destination, eg next free row in another worksheet.
So you end up with something like …
Sub DeleteCompleted()
Dim ToDoList As ListObject
Dim ListRowCount As Long
Dim iRow As Long
Set ToDoList = Sheets("To Do Lijst").ListObjects("Table2")
ListRowCount = ToDoList.ListRows.Count
Dim NextFreeRow As Range
For iRow = ListRowCount To 1 Step -1
With ToDoList
If .DataBodyRange(iRow, 4).Value = "Voltooid" Then
Set NextFreeRow = Worksheets("Destination").Cells(Rows.Count, 4).End(xlUp).Offset(1, -3)
ToDoList.ListRows(iRow).Range.Copy Destination:=NextFreeRow
ToDoList.ListRows(iRow).Range.Delete Shift:=xlUp
End If
End With
Next iRow
End Sub
I´m trying to do a VLOOKUP of a column data set at a Sheet called "SyS" in G column. and I'd like to Vlookup relevant data using columns in another sheet called "CONF_mapping", located in the same Workbook. I need to find my data located at the range ("A1:E65000") (It's at column A, but I need to retrieve data from other columns with my vlookup to SyS). I'm not getting good results with my code, and I beg your pardon, it´s my first question in the forum.
Worksheets("SyS").Select
Dim wsThis As Worksheet
Dim aCell As Range
Sheets("CONF_mapping").Columns(2).Copy Destination:=Sheets("SyS").Columns(8)
Set wsThis = Sheets("SyS")
With wsThis
For Each aCell In .Range("A1:E65000")
'.Cells(aCell.Row, 8) = "Not Found"
On Error Resume Next
.Cells(aCell.Row, 8) = Application.WorksheetFunction.VLookup( _
aCell.value, wsThat.Range("G2:G65000"), 2, False)
On Error GoTo 0
Next aCell
End With
Worksheets("SyS").Select
I have find this code but I was not able to make it works for me.
I would appreciate any help.
You have mistake here:
VLookup(aCell.value, wsThat.Range("G2:G65000"), 2, False)
Range "G2:G65000" Have just 1 column G, but you try to get column#2 which does not exists.
UPD:
I guess you need something like this:
Const COLUMN_TO_MATCH_IN_SYS = 8
Const COLUMN_TO_MATCH_IN_CONF = 1
Sub test()
Dim wsSys As Worksheet
Dim wsConf As Worksheet
Set wsSys = Sheets("SyS")
Set wsConf = Sheets("CONF_mapping")
Dim RowSys As Range
Dim RowConf As Range
For Each RowSys In wsSys.UsedRange.Rows
For Each RowConf In wsConf.UsedRange.Rows
If RowSys.Cells(1, COLUMN_TO_MATCH_IN_SYS) = _
RowConf.Cells(1, COLUMN_TO_MATCH_IN_CONF) Then
' Copy row values which is needed
RowSys.Cells(1, 6) = RowConf.Cells(1, 1) ' From column A(conf) to G(sys)
RowSys.Cells(1, 7) = RowConf.Cells(1, 2) ' From column B(conf) to H (sys)
End If
Next aCell
Next
End Sub
With this solution you don't need to search the Range for each cell (just for each row), so it will work 5 times faster.
Just started a new job. I'm automating a month-end report and I'm new at VBA. Been googling most of my issues with success, but I've finally run into a wall. In essence I'm downloading some data from SAP and from there I need to build a report.
My question is: How to do a sumif function using loops in VBA?
Data pull:
Sheet1 contains a product code and purchase amounts (columns A & B) respectively. One product code can have several purchases (several rows with the same product code).
Steps so far:
I arranged the data sheet1 to be in ascending order.
Copied unique values for the product codes onto another sheet (sheet2). So Sheet2 has a list of all the products (in ascending order).
I want to get the sum of all purchases in sheet2 column B (per product code). I know how to do this using formulas, but I need to automate this as much as possible. (+ I'm genuinely interested in figuring this out)
This is what I did in VBA so far:
Sub Macro_test()
Dim tb As Worksheet
Dim tb2 As Worksheet
Dim x As Integer
Dim y As Integer
Dim lrow As Long
Set tb = Sheets("sheet1")
Set tb2 = Sheets("sheet2")
lrow = tb.Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To lrow
For y = 2 To lrow
If tb2.Cells(x, 1).Value = tb.Cells(y, 1).Value Then
tb2.Cells(x, 2).Value = tb.Cells(y, 2).Value
End If
Next y
Next x
End Sub
If i'm not mistaken, for each product_code in sheet2 col A, I'm looping through all the product codes in sheet1 and getting back the LAST value it finds, instead of the sum of all values... I understand why it doesn't work, I just don't know how to fix it.
Any help would be much appreciated. Thanks!
This statement overwrites the value of tb2.Cells(x, 2).Value at each iteration:
tb2.Cells(x, 2).Value = tb.Cells(y, 2).Value
Instead, I think you need to keep adding to it:
tb2.Cells(x, 2).Value = tb2.Cells(x, 2).Value + tb.Cells(y, 2).Value
But I don't like the looks of your double-loop which uses only one lrow variable to represent the "last row" on the two different worksheets, that could be causing some issues.
Or, in your loop do something like this which I think will avoid the duplicate sum. Still, assumes the second worksheet doesn't initially have any value in
' Base our lRow on Sheet2, we don't care how many rows in Sheet1.
lrow = tb2.Cells(tb2.Rows.Count, 1).End(xlUp).Row
Dim cl as Range
Set cl = tb.Cells(2,1) 'Our initial cell value / ID
For x = 2 to lRow '## Look the rows on Sheet 2
'## Check if the cell on Sheet1 == cell on Sheet2
While cl.Value = tb2.Cells(x,1).Value
'## Add cl.Value t- the tb2 cell:
tb2.Cells(x, 2).Value = tb2.Cells(x, 2).Value + cl.Offset(0,1).Value
Set cl = cl.Offset(1) '## Reassign to the next Row
Wend
Next
But it would be better to omit the double-loop and simply use VBA to do 1 of the following:
1. Insert The Formula:
(See Scott Holtzman's answer).
This approach is better for lots of reasons, not the least of which is that the WorksheetFunction is optimized already, so it should arguably perform better though on a small dataset the difference in runtime will be negligible. The other reason is that it's stupid to reinvent the wheel unless you have a very good justification for doing so, so in this case, why write your own version of code that accomplishes what the built-in SumIf already does and is specifically designed to do?
This approach is also ideal if the reference data may change, as the cell formulas will automatically recalculate based on the data in Sheet1.
2. Evaluate the formula & replace with values only:
If you prefer not to retain the formula, then a simple Value assignment can remove the formula but retain the results:
With .Range(.Range("B2"), .Range("A2").End(xlDown).Offset(, 1))
.FormulaR1C1 = "=SUMIF(Sheet1!C[-1]:C[-1],RC[-1],Sheet1!C:C)"
.Value = .Value 'This line gets rid of the formula but retains the values
End With
Use this approach if you will be removing Sheet1, as removing the referents will break the formula on Sheet2, or if you otherwise want the Sheet2 to be a "snapshot" instead of a dynamic summation.
If you really need this automated, take advantage of VBA to place the formula for you. It's very quick and easy using R1C1 notation.
Complete code (tested):
Dim tb As Worksheet
Dim tb2 As Worksheet
Set tb = Sheets("sheet1")
Set tb2 = Sheets("sheet2")
Dim lrow As Long
lrow = tb.Cells(tb.Rows.Count, 1).End(xlUp).Row
tb.Range("A2:A" & lrow).Copy tb2.Range("A2")
With tb2
.Range("A2").CurrentRegion.RemoveDuplicates 1
With .Range(.Range("B2"), .Range("A2").End(xlDown).Offset(, 1))
.FormulaR1C1 = "=SUMIF(Sheet1!C[-1]:C[-1],RC[-1],Sheet1!C:C)"
End With
End With
Note that with R1C1 notation the C and R are not referring to column or row letters . Rather they are the column and row offsets from the place where the formula is stored on the specific worksheet. In this case Sheet!C[-1] refers to the entire A column of sheet one, since the formula is entered into column B of sheet 2.
I wrote a neat little algorithm (if you can call it that) that does what you want them spits out grouped by totals into another sheet. Basically it loops through the first section to get unique names/labels and stores them into an array. Then it iterates through that array and adds up values if the current iteration matches what the current iteration of the nested loop position.
Private Sub that()
Dim this As Variant
Dim that(9, 1) As String
Dim rowC As Long
Dim colC As Long
this = ThisWorkbook.Sheets("Sheet4").UsedRange
rowC = ThisWorkbook.Sheets("Sheet4").UsedRange.Rows.Count
colC = ThisWorkbook.Sheets("Sheet4").UsedRange.Columns.Count
Dim thisname As String
Dim i As Long
Dim y As Long
Dim x As Long
For i = LBound(this, 1) To UBound(this, 1)
thisname = this(i, 1)
For x = LBound(that, 1) To UBound(that, 1)
If thisname = that(x, 0) Then
Exit For
ElseIf thisname <> that(x, 0) And that(x, 0) = vbNullString Then
that(x, 0) = thisname
Exit For
End If
Next x
Next i
For i = LBound(that, 1) To UBound(that, 1)
thisname = that(i, 0)
For j = LBound(this, 1) To UBound(this, 1)
If this(j, 1) = thisname Then
thisvalue = thisvalue + this(j, 2)
End If
Next j
that(i, 1) = thisvalue
thisvalue = 0
Next i
ThisWorkbook.Sheets("sheet5").Range(ThisWorkbook.Sheets("Sheet5").Cells(1, 1), ThisWorkbook.Sheets("Sheet5").Cells(rowC, colC)).Value2 = that
End Sub
Yay arrays
I have 2 workbooks and am trying to find a way to copy a column from wb1 into wb2. I am aware I could just copy/paste, but the idea is to make something so my boss can click the macro and everything populates.
I have been trying a code I came across in another question:
Sub Import()
Dim sourceColumn As Range
Dim destColumn As Range
Set sourceColumn = Workbooks("C:\Documents and Settings\********\My Documents\*********.xlsm").Worksheets(2).Columns("BL")
Set destColumn = Workbooks("C:\Documents and Settings\********\My Documents\*********.xlsm").Worksheets(2).Columns("A")
sourceColumn.Copy Destination = destColumn
End Sub
When I run this, I get a "Subscript Out Of Range" Error.
The source column contains a formula relying on other columns and the destination column is empty, but even when I ran this on dummy workbooks with small digits I got the same error.
Is there something super basic I am missing here?
EDIT : Just realized what's probably missing from that piece of code you have. Add a ":" in there! Change to destination:=Workbooks(".... and it should work fine.
Extra details : When working with function parameters, you have to add the ":" in order to specify to the computer you're not evaluating an equality, but doing a parameter assignment.
(Old, flashy answer)
As I assume this is what you're trying to do; this script will probably do what you want. The style will NOT be preserved however.
Sub yourSub()
Application.ScreenUpdating = False 'Disables "Screen flashing" between 2 workbooks
Dim colA As Integer, colB As Integer
Dim rowA As Integer, rowB As Integer
Dim wbA As Workbook, wbB As Workbook
Set wbA = ThisWorkbook
Set wbB = Workbooks.Open("C:/YourFilePath/YourFile.xls")
colA = 1 'Replace "1" with the number of the column FROM which you're copying
colB = 1 'Replace "1" with the number of the column TO which you're copying
rowA = 1 'Replace "1" with the number of the starting row of the column FROM which you're copying
rowB = 1 'Replace "1" with the number of the row of the column TO which you're copying
wbA.Activate
lastA = Cells(Rows.Count, colA).End(xlUp).Row 'This finds the last row of the data of the column FROM which you're copying
For x = rowA To lastA 'Loops through all the rows of A
wbA.Activate
yourData = Cells(x, colA)
wbB.Activate
Cells(rowB, colB) = yourData
rowB = rowB + 1 'Increments the current line of destination workbook
Next x 'Skips to next row
Application.ScreenUpdating = True 'Re-enables Screen Updating
End Sub
I didn't have time to test this yet. Will do as soon as possible.