Best Way to Create Import Files with Excel? - vba

Not sure what the best way to describe this situation is, but I'll do my best. I am trying to create an import file for a database system using Excel.
Here is what we have as an example:
1) Names in a Column
2) Accounts in a Column (Accounts 1-3)
3) Amounts (An Amount for each Combo, ex: Name1, Account1, Name 1, Account2)
So what I want is an easy way (possibly with VBA?) to create an import file similar to below:
(Columns A, B, C)
Name#1, Account#1, Amount#1
Name#1, Account#2, Amount#2
Name#1, Account#3, Amount#3
Name#2, Account#1, Amount#4
Name#2, Account#2, Amount#5
etc.. etc..
Is there anyway to do this without having to do a ton of copying and pasting? I tried Pivot Tables, but it just doesn't seem to work for my situation
Sample Data:
Names | Accounts | Amounts
David 11230 $32.50
Marry 11240 $2.00
Jerry 54500 $990.00
64000 $500.00
$300.00
$600.00
$330.55
$500.00
$45.00
$53.38
$75.00
$44.00
Thus the intended output we want is:
David, 11230, $32.50
David, 11240, $2.00
David, 54500, $990.00
David, 64000, $500.00
Marry, 11230, $300.00
Marry, 11240, $600.00
....Continue...
Hope that helps

This worked for me:
Sub tester()
Dim sht As Worksheet, rngNames As Range, rngAccts As Range
Dim nm As Range, acct As Range, i As Long
Set sht = ActiveSheet
With sht
Set rngNames = .Range(.Range("A2"), _
.Cells(.Rows.Count, 1).End(xlUp))
Set rngAccts = .Range(.Range("B2"), _
.Cells(.Rows.Count, 2).End(xlUp))
End With
i = 1
For Each nm In rngNames.Cells
For Each acct In rngAccts.Cells
i = i + 1
sht.Cells(i, 5).Value = nm.Value
sht.Cells(i, 6).Value = acct.Value
sht.Cells(i, 7).Value = sht.Range("C1").Offset(i - 1, 0).Value
Next acct
Next nm
End Sub
Inputs are in ColA-C, output goes to Cols E-G

Related

Merge two Excel tables (using VBA)

I have been trying to figure out how to merge two tables from the same workbook into a third one using VBA. Example :
Worksheet1:
From To Value
Italy Japan 1000
France Japan 500
Canada Japan 0
France Italy 700
Worksheet2:
From To Value
Italy Japan 5555
France Japan 1111
Canada Japan 777
Canada France 333
Disired output (worksheet3):
From To Value1 Value2
Italy Japan 1000 5555
France Japan 500 1111
Canada Japan 0 777
France Italy 700
Canada France 333
I would need a VBA solution since the original tables are about 400 rows long, and I would need to perform the same operation for several workbooks. I would be very grateful for any suggestion regarding this problem !
Edit:
In case it is of interest to anyone, I managed to make a working code. Worksheet1 was a nickname for "List Import" and Worksheet2 is "List Export". In both sheets, I inserted a column (C) that states both countries. I used that new column and the values to build the table in Worksheet3 (now "Combolist").
Sub combolist()
Dim lastRowImp As Long, lastRowExp As Long, startPaste As Long, endPaste As Long
Dim ws As Worksheet, Lookup_Range As Range, i As Integer
Dim lastRow As Long
lastRowImp = Sheets("List Import").Cells(Rows.Count, 1).End(xlUp).Row
lastRowExp = Sheets("List Export").Cells(Rows.Count, 1).End(xlUp).Row
startPaste = lastRowImp + 1
endPaste = lastRowImp + lastRowExp - 1
'add a new sheet and headers
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = "Combolist"
Sheets("Combolist").Range("B1") = "Import"
Sheets("Combolist").Range("C1") = "Export"
Sheets("Combolist").Range("C1").EntireRow.Font.Bold = True
'copy flows from import and export list
Sheets("Combolist").Range("A1:A" & lastRowImp) = Sheets("List Import").Range("C1:C" & lastRowImp).Value
Sheets("Combolist").Range("A" & startPaste & ":A" & endPaste) = Sheets("List Export").Range("C2:C" & lastRowExp).Value
'remove duplicates
lastRow = Sheets("Combolist").Cells(Rows.Count, 1).End(xlUp).Row
Sheets("Combolist").Range(Cells(1, 1), Cells(lastRow, 1)).RemoveDuplicates Columns:=Array(1), Header:=xlYes
Set ws = ActiveWorkbook.Sheets("Combolist")
lastRow = Sheets("Combolist").Cells(Rows.Count, 1).End(xlUp).Row
'populate Import values
Set Lookup_Range = Sheets("List Import").Range("C1:D" & lastRowImp)
With ws
For i = 2 To lastRow
On Error Resume Next
If Application.WorksheetFunction.VLookup(ws.Cells(i, 1), Lookup_Range, 2, False) = "" Then
ws.Cells(i, 2) = 0
Else
ws.Cells(i, 2) = Application.WorksheetFunction.VLookup(ws.Cells(i, 1), Lookup_Range, 2, False)
End If
Next i
End With
'populate Export values
Set Lookup_Range = Sheets("List Export").Range("C1:D" & lastRowExp)
With ws
For i = 2 To lastRow
On Error Resume Next
If Application.WorksheetFunction.VLookup(ws.Cells(i, 1), Lookup_Range, 2, False) = "" Then
ws.Cells(i, 3) = 0
Else
ws.Cells(i, 3) = Application.WorksheetFunction.VLookup(ws.Cells(i, 1), Lookup_Range, 2, False)
End If
Next i
End With
End Sub
While this could be solved with VBA, you're likely to be better off using formulas (unless you must do this very frequently). The VBA solution will require some know-how, and even more if you want to be able to maintain the solution.
An Excel formula would be quite simple. First, create a UniqueID column:
UniqueID From To Value
Italy_Japan Italy Japan 1000
France_Japan France Japan 500
Canada_Japan Canada Japan 0
France_Italy France Italy 700
Canada_France Canada France
You would do the same thing for both tables. Next, get all of the unique UniqueID's. For this, you could use Data > Remove Duplicates, just be sure to make a copy before removing duplicates, otherwise you are removing records from your source. Put this list of UniqueID's into a new Table. Keep in mind that all of this will be easier if all of your data is in Table format (you'll see a Table tab in the ribbon when inside of the table range.
If you need to format your data as a table, go to the worksheet, press CTRL+HOME (this goes to the very first cell). If your first cell is in another location, just navigate there instead. If your table is the only data on the worksheet, try using CTRL+SHIFT+END from here to highlight to the last used cell. Otherwise, a combination of CTRL+SHIFT+RIGHT and CTRL+SHIFT+DOWN will get you what you need. Finally, name your table for the love of all that is excel, this one simple habit saves a ton of time. For my example I will assume that you have a Primary and Secondary table.
Our formula in our combined table would then look something like this:
=IfError(Vlookup([UniqueID], Primary, Column(Primary[Value]), False), "")
Or, if your Primary table doesn't start in the first column, use this:
=IfError(Vlookup([UniqueID], Primary, 4, False), "")
The difference here is that the former will change the index as the column is moved, the latter will not, and must be edited if the table is edited.
Do the same thing in your next column for the other table:
=IfError(Vlookup([UniqueID], Secondary, Column(Primary[Value]), False), "")
=IfError(Vlookup([UniqueID], Secondary, 4, False), "")
This will 'merge' the two sets based on the shared UniqueID and will leave blanks if the record doesn't exist. Learning how to do this may be less convenient than the learning how to do it in VBA, but I would strongly dissuade you from trying to learn VBA if you can't use an implementation like this.
To be clear, the reason why the formula approach is ideal in this instance is that the task you are asking for help with is very simple, and you would be better developing your Excel skills since, doing so, will allow you to solve similar tasks much faster in the future. Even a novice could implement this solution within 15 minutes or so, where it would easily take you days to learn a scalable VBA solution.

Double For Each Loop (How to work around this issue?) Excel VBA

Background info: The aim of my tool is I have a form where when you enter a name in a cell and it brings up all of the details attached to that persons name using vlookups and basic excel code.
Now what I am doing is I would like to click a button and make vba run all the names through this tool so the details from the form are all stored in a table. The code below returns the first column of data from the first box in a For Each Loop (which this is doing fine if the second for loop is removed). The problem I have is I need a second for each loop to return a second column worth of data but the problem with this is the first for each loop only runs once and then it will run the second for each loop multiple times to return the second column of data that I need. What I need is either 1 for each loop which can take 2 ranges or a completely different way to do this. Any help would be much appreciated.
Public Sub Button1_Click()
Application.ScreenUpdating = True
Dim copySheet As Worksheet
Dim pasteSheet As Worksheet
Dim r As Range
Dim h As Range
Set copySheet = Worksheets("WIN RATES")
With copySheet
For Each r In .Range("H3", .Range("H" & Rows.Count).End(xlUp))
If Len(r) > 0 Then
Worksheets("NEW! FORM CHARTS").Range("E4").Value = r.Value
Worksheets("NEW! FORM CHARTS").Range("E4").Resize(, 1).Copy
Worksheets("Full Over 2.5 & BTTS list").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
With copySheet
For Each h In .Range("N3", .Range("N" & Rows.Count).End(xlUp))
If Len(h) > 0 Then
Worksheets("NEW! FORM CHARTS").Range("M4").Value = h.Value
Worksheets("NEW! FORM CHARTS").Range("M4").Resize(, 1).Copy
Worksheets("Full Over 2.5 & BTTS list").Cells(Rows.Count, 2).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
End If
Next h
End With
End If
Next r
End With
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
The way I want it returned is like this:
Name 1 | Name 2
tom | 17846
mike | 16253
steve | 10987
Anne | 16243
But, understandingly, my data is doing this:
Name 1 | Name 2
tom | 17846
| 16253
| 10987
| 16243
The idea is that excel will run through all the names in the list and fill in the form with Name 1 and Name 2, together with these names input onto the form, they will fill in the rest of the form with the vlookups in the excel sheet itself, so my end goal is to have this kind of table where vlookup1 and vlookup2 is from the excel sheet:
Name 1 | Name 2 | VLOOKUPDATA1 | VLOOKUPDATA2
tom | 17846 | 1 | 80%
mike | 16253 | 8 | 90%
steve | 10987 | 6 | 23%
Anne | 16243 | 3 | 43%
I know this is long winded, just ask me if you need any clarification.
You don't need two loops, just the one acquiring data from columns "H" and "N" in each iteration. With that much data it'll take an awfully long time copying and pasting one cell at a time - you'd be far better reading to and writing from an array.
Code below shows both points. I really can't see why you're writing each item to the "NEW! FORM CHARTS" worksheet only to write over it with the next loop, so I've left that part out of your code. You'll see that there's a small bit of extra coding that just deals with the case that the two columns don't end on the same row.
I'd also recommend you read about classes as this will vastly simplify and probably speed up your tasks.
Dim home As Variant
Dim away As Variant
Dim r As Long, rMax As Long, rOffset As Long
Dim output() As Variant
With ThisWorkbook.Worksheets("WIN RATES")
home = .Range(.Range("H3").End(xlDown), .Range("H" & .Rows.Count).End(xlUp)).Value2
away = .Range(.Range("N3").End(xlDown), .Range("N" & .Rows.Count).End(xlUp)).Value2
End With
rMax = WorksheetFunction.Max(UBound(home, 1), UBound(away, 1))
ReDim output(1 To rMax, 1 To 2)
For r = 1 To rMax
If r <= UBound(home, 1) Then output(r, 1) = home(r, 1)
If r <= UBound(away, 1) Then output(r, 2) = away(r, 1)
Next
With ThisWorkbook.Worksheets("Full Over 2.5 & BTTS list")
rOffset = WorksheetFunction.Max(.Range("A1").End(xlUp).Row, .Range("A2").End(xlUp).Row)
.Range("A1").Offset(rOffset).Resize(UBound(output, 1), UBound(output, 2)).Value = output
End With

Copy and paste data data from another sheet and autofill based on another column

I have data to copy from sheet 1 ("Invoice") to sheet 2 ("Inventory"). I only need to copy 1 cell consist of numbers of Invoice ("1,2,3,etc").
I already succeed copy (Invoice.D14) and paste the cells (Inventory.B4) however i need to autofill as far as another column (Inventory.D4) in sheet 2 ("Inventory"). Please see my code as follow :
Dim Outgoing As Worksheet
Dim Invoice As Worksheet
Set Outgoing = ActiveWorkbook.Worksheets("Inventory")
Set Invoice = ActiveWorkbook.Worksheets("Invoice")
Invoice.Range("D14").Copy
Outgoing.Cells(Rows.Count, 1).Range("B1").End(xlUp).Offset(1, 0).PasteSpecial
With Range("B4").Resize(4)
.Value = [B4]
.AutoFill .Resize(Range("C" & Rows.Count).End(xlUp).Row - 1)
End With
It keeps coming with error message.
Anyone can help?
EDIT MESSAGE
I already copied from sheet 1 ("Invoice") to sheet 2 ("Inventory").
On sheet 1 ("Invoice") contains :
Data code with range from B21 until B27
Transaction ID with cell D14
I copy above data to sheet 2 ("Inventory") with detail :
Data code copy to Inventory.columnD (start row at 4)
Transaction ID copy to inventory.columnB (start row at 4)
If i input 3 code on data code it will copy to Inventory with 3 code as well (3 rows), however since i only copy 1 cell for transaction ID the output on Inventory only 1 cell as well.
What i need is to autofill the transaction ID as long as data code that i input previously. However, if i'm using autofill i don't know why it's always shown error message. So i try a different way, to copy the first transaction ID.
I try below code as well :
Sub Outgoing_Data()
Dim Inventory As Worksheet
Dim Invoice As Worksheet
Dim columnB As Range
Dim columnD As Range
Dim c As Range
Dim i As Long
Dim lastNonEmptyRow As Range
Set Outgoing = Worksheets("Inventory")
Set Invoice = Worksheets("Invoice")
Set columnB = Range("B:B")
Set columnD = Range("D:D")
Invoice.Range("B21:B27").Copy
Outgoing.Cells(Rows.Count, 1).Range("D1").End(xlUp).Offset(1, 0).PasteSpecial
Invoice.Range("D14").Copy
Outgoing.Cells(Rows.Count, 1).Range("B1").End(xlUp).Offset(1, 0).PasteSpecial
i = 5
Set lastNonEmptyRow = Outgoing.Range(Cells(i - 1, 2), Cells(i - 1, 2))
For Each c In columnD.Cells
If c.Value2 = "" Then Exit For
i = i + 1
Next c
Do While columnD(i) <> ""
lastNonEmptyRow.Copy Range(Cells(i, 2), Cells(i, 2)).PasteSpecial
i = i + 1
Loop
End Sub
The output that i want it :
| transaction_ID | Product ID
| 1 | 2DFGH4
| 1 | 2DFGH7
| 1 | 2HJTY0
| 2 | 1JKTY7
| 2 | 5THSD1
| 3 | 4GHTY9
(Have no idea how to draw a table in here, but hope you understand what i'm saying)
The result, transaction ID already as long as data code. For the first trial it looks fine. But when i change the transaction ID on Invoice and run it again without delete the data on Inventory, the transaction ID on Inventory only copy on 1 cell. Is there any suggestion for this?
Really appreciate your answers.
Thank you
Maybe the wrong sheet is activated? Try:
With Outgoing.Range("B4").Resize(4)
.Value = [B4]
.AutoFill .Resize(Range("C" & Rows.Count).End(xlUp).Row - 1)
End With
EDIT:
The problem with .AutoFill .Resize(Range("C" & Rows.Count).End(xlUp).Row - 1) is that Rows.Count returns absolute value of rows, counting form 1, not 4 (which is the row that you are refering to). So, if you always start autofill form B4, simple -3 will work fro you:
.AutoFill .Resize(Range("C" & Rows.Count).End(xlUp).Row - 3)
But, if this row would change dynamically, you can add this to row calculation, for example:
expectedRow = 8
With Outgoing.Range("B" & expectedRow).Resize(4)
.Value = Range("B" & expectedRow).Value
.AutoFill .Resize(Range("C" & Rows.Count).End(xlUp).Row - expectedRow + 1)
End With
By the way, I don't uderstand the purpose of this .Value = Range("B" & expectedRow).Value line and Resize(4) part of With command, but I don't know how your data looks like and what is your particular goal, so try to remove this and see if output works out for you.
your aim is not very clear
try this:
Option Explicit
Sub main()
Dim Outgoing As Worksheet
Dim Invoice As Worksheet
Set Outgoing = ActiveWorkbook.Worksheets("Inventory")
Set Invoice = ActiveWorkbook.Worksheets("Invoice")
Invoice.Range("D14").Copy
With Outgoing
With .Cells(Rows.Count, 1).Range("B1").End(xlUp).Offset(1, 0)
.PasteSpecial
.Resize(4).Value = .Value
.Resize(4).AutoFill .Resize(4, 2)
End With
End With
End Sub

vba: Identify row using 2 criteria and return other data from row

I am trying to find code that looks at two criteria in spreadsheet1 and finds a row that corresponds in spreadsheet2 and returns a third piece of data in spreadsheet2 to spreadsheet1. I need to do this in vba because it loops, because I will be done it again and again, and because the data from spreadsheet2 imports from another database and will change over time. If possible it would be nice if the code also allowed for identifying a 3rd criteria on spreadsheet2.
Example is:
Spreadsheeet 1
Product ID ActCode: A0003
11111
12345
22222
...
Spreadheet 2
ProductID ActivityCode DateDue
11111 A0001 7/15/15
11111 P7530 7/30/15
11111 A0003 8/1/15
12345 A0003 12/15/15
12345 A0007 1/1/15
22222 A0001 2/1/15
...
Where I want Spreadsheet1 to end up:
Spreadsheeet 1
Product ID ActCode: A0003
11111 8/1/15
12345 12/15/15
22222 -
...
I have tried a ton of things over the past few days. 1) vlookup/index/match combos that have never really worked, 2) filtering spreadsheet2 by productID and activitycode and then copying to spreadsheet1 the visible cells - this works but is very slow. I will be doing this for many activity codes, so I need something faster (I can post the code if you want to see it). I am currently trying a loop within a loop. Not sure if this is the best way but here is the code I have so far. It does copy some dates over, but not the right ones - its also a bit slow.
Sub test()
Application.ScreenUpdating = False
Sheets("Spreadsheet1").Select
Range("A2").Select ' Select A = the column with the product ID in it
' Set Do loop to stop when an empty cell is reached.
Do Until IsEmpty(ActiveCell)
Dim ConceptAct As String
ConceptAct = "A0003"
Dim ProductID
ProductID = ActiveCell.Value
Dim ConcDue
Sheets("Spreadsheet2").Select
Range("A2").Select 'The column with the ProductID in it
Do Until IsEmpty(ActiveCell)
If ActiveCell.Value = ProductID And ActiveCell.Offset(0, 1).Value = ConceptAct Then
ConcDue = ActiveCell.Offset(0, 2).Value
Exit Do
End If
ActiveCell.Offset(1, 0).Select
Loop
Sheets("Spreadsheet1").Select
ActiveCell.Offset(0, 1) = ConcDue
' Step down 1 row from present location.
ActiveCell.Offset(1, 0).Select
Loop
Application.ScreenUpdating = True
End Sub
Why won't Index/Match work? I was able to get, I think, your solution with an Index/Match formula entered as an array. Here's a screenshot of everything:
Index/Match can use multiple criteria for looking things up, just connect these with &, both in the first and second parts of the Match(), and hit CTRL+SHIFT+ENTER to enter as an array. This formua will look at the product ID, then that ActCode, return the date.
Is this what you were looking for?
I'm unclear on why a two-column criteria formula using native worksheet functions is not apprpriate but a User Defined Function (aka UDF) would be one avenue to pursue from a VBA point of view.
Function udf_Get_Two(sCode As Range, rCodes As Range, _
sProd As Range, rProds As Range, _
rDates As Range)
Dim vCode As Variant, rw As Long, m As Long
'quick check to see if there is anything to find
If CBool(Application.CountIfs(rCodes.Columns(1), sCode.Value, _
rProds.Resize(rCodes.Rows.Count, 1), sProd.Value)) Then
rw = 0
For m = 1 To Application.CountIf(rCodes.Columns(1), sCode.Value)
rw = rw + Application.Match(sCode.Value, rCodes.Columns(1).Resize(rCodes.Rows.Count - rw, 1).Offset(rw, 0), 0)
If rProds(rw, 1) = sProd Then
udf_Get_Two = rDates.Cells(rw, 1).Value
Exit Function
End If
Next m
End If
End Function
Use like any other worksheet formula. Example:
=udf_Get_Two(A2, Sheet2!$A$2:$A$7, $C$1, Sheet2!$B$2:$B$7, Sheet2!$C$2:$C$7)
    
Note that the returned values are raw. The cells should be formatted as m/d/yy or as you prefer.

Move cells in same workbook with loop

I have the following problem:
an excel sheet with an unknown amount of rows, and in each row an unknown amount of products with the amount of products sold.
For example: 1 Coke 2 Fanta,...
Where for example 1 is situated in A1, Coke in B1, 2 in C1,...
Now I would like to get all amounts in Column A and all products in Column B, but each amount has to stay linked to its product!
Since I dont know how many rows there are per day, this should be a loop until there is no more data in "Axx"
Is there somebody who could help me out? thanks!
Whilst I do agree with Alex D, you gotta give us something to work with! but I also know the anger macros can give, so heres a code all youve got to do is write the list of products in column C. This will allow for a varying amount
Sub Sample()
Dim LastRow As Long
Dim ProductLastRow As Long
ProductLastRow = Sheets("Sheet1").Cells(Rows.Count, "C").End(xlUp).Row
LastRow = Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row 'will find last row
Set myrng1 = Sheets("Sheet1").Range("B1:B" & LastRow) 'Will find the product name
Set myrng2 = Sheets("Sheet1").Range("A1:A" & LastRow) 'Will find product amount
Range("D1:D" & ProductLastRow).FormulaR1C1 = "=SUMIF(" & myrng1.Address(ReferenceStyle:=xlR1C1) & ",RC[-1]," & myrng2.Address(ReferenceStyle:=xlR1C1) & ")"
End Sub
Chin Up!