How to merge two excel sheets using VBA? - excel

I have an Excel workbook with two sheets.
The range for the first sheet is: A1:AU
And the range for the second sheet is: B1:I
I don't want to keep A1 from the second sheet because they share the same ID column. I am using VBA in MS Access. And what I am trying to do is create a temp file from an original file, then edit it. I've been able to do this with the following code:
public function copyFile(newFilePath As String)
objFile.SaveCopyAs newFilePath
' Open the copied file
Dim copiedFile As Object
Set copiedFile = objExcel.Workbooks.Open(newFilePath)
End Function
But when I try to merge sheet2 and sheet3 together like so:
public function copyFile(newFilePath As String)
objFile.SaveCopyAs newFilePath
' Open the copied file
Dim copiedFile As Object
Set copiedFile = objExcel.Workbooks.Open(newFilePath)
Dim sheet2 As Object
Set sheet2 = copiedFile.Worksheets("sheet2")
Dim sheet3 As Object
Set sheet3 = copiedFile.Worksheets("sheet3")
' Determine last used column in sheet 2
Dim lastCol As Long
lastCol = sheet2.Cells(1, Columns.Count).End(xlToLeft).Column
Dim lastRow2 As Long
lastRow2 = sheet2.Cells(Rows.Count, "A").End(xlUp).Row
sheet3.Range("B1:I" & sheet3.Cells(Rows.Count, "B").End(xlUp).Row).Copy _
sheet2.Cells(1, lastCol + 1).Offset(lastRow2, 0)
copiedFile.Save
copiedFile.Close False
End Function
I run into this issue where all is does it create a wide cell in sheet 2 at the start of the first empty row of the excel file.
Essentially, what I'd like to do is if this is sheet 2:
col1 | col2 | col3 ...
abc | $45 | ghi
and this is sheet 3:
colx | coly | colz ...
ytr | tre | $300
Then after processing sheet 2 should look like:
col1 | col2 | col3 | colx | coly | colz
abc | $45 | ghi | ytr | tre | $300
But so far my attempts at getting this to work have failed.
Any ideas are greatly appreciated!

Looks like you're just getting mixed up where to start and stop the copy at
This should work according to your problem description
It assumes the number of rows and ID values in both sheets are the same.
No sorting or checking required?
With sheet2
.Activate
.Range("A1").Select
.Range(Selection, Selection.End(xlToRight)).Select
End With
lastCol = Selection.Columns.Count + 1
With sheet3
.Activate
.Range("B1").Select
.Range(Selection, Selection.End(xlToRight)).Select
.Range(Selection, Selection.End(xlDown)).Select
End With
Selection.Copy
sheet2.Cells(1, lastCol).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False

If these worksheets are really a simple structure with row 1 containing column headers and common identifier column (I call it ID), consider:
Sub getExcelData2()
Dim rs As ADODB.Recordset
Dim cn As ADODB.Connection
'set connection to workbook and open recordset
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=path to folder\name.xlsx" _
& ";Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1"";"
rs.Open "Select [Sheet2$].*, [Sheet3$].* FROM [Sheet2$] " & _
"INNER JOIN [Sheet3$] ON [Sheet2$].ID=[Sheet3$].ID", cn, adOpenStatic, adLockOptimistic, adCmdText
'open Excel application, add new workbook, write recordset to sheet
Dim xlApp As Object
Dim xlWbk As Object
Dim x As Integer
Set xlApp = CreateObject("Excel.Application")
Set xlWbk = xlApp.Workbooks.Add
For x = 1 To rs.Fields.Count
xlWbk.worksheets(1).Cells(1, x) = rs(x - 1).Name
Next
xlWbk.worksheets(1).Range("A2").CopyFromRecordset rs
xlApp.Visible = True
xlApp.UserControl = True
End Sub
This uses INNER JOIN which assumes same ID values in both sheets. If that is not the case, perhaps LEFT or RIGHT JOIN will produce correct dataset. If not, the SQL statement will be more complicated.
SELECT Q1.ID, [Sheet2$].*, [Sheet3$].*
FROM [Sheet3$] RIGHT JOIN ([Sheet2$] RIGHT JOIN (SELECT ID FROM [Sheet2$]
UNION SELECT ID FROM [Sheet3$]) AS Q1 ON [Sheet2$].ID = Q1.ID) ON [Sheet3$].ID = Q1.ID;
In both examples, wildcards are used to retrieve fields because there are so many columns. This means ID data will be in multiple columns but user can ignore or delete the duplicates. Or you can expand code to find the duplicate columns and delete, like:
For x = 2 To 3
xlWbk.worksheets(1).Columns(xlWbk.worksheets(1).Cells.find(x & "$").Column).Delete
Next
Also assumes all fields in sheets are required and total fields will not exceed 255.
Should be able to replace sheet references with Named Range references - I did not test.

Related

Inserting a column

I am hoping someone could give me some guidance here. My situation is this:
One worksheet with 100 rows of data in a table
Each cell in Column A holds one of two values - either CSv1 or CSv2
Column B holds a specific case_number
I want to insert a column at position 4 (Column D) with the label 'Caselink'
In location D2 I am trying to insert a hyperlink that builds off A2 and B2 where the link is dependent on the value in both. (they go to two different sites depending on column A). Then to populate down with relative location to row numbers... Here is what I have so far, but it gives me an error on the 'Else' statement saying I havean 'Else without If'.
If I take out the else statement and follow-on formula, and leave only the first If formula, it will populate all cells in the D column with the link for the CSv1 value.
Thoughts?
Sub InsertHyperlink_EscFeedback()
With ActiveSheet
.ListObjects(1).Name = "Drilldown"
End With
Dim ws As Worksheet
Set ws = ActiveSheet
Dim target_table As ListObject
Set target_table = ws.ListObjects("Drilldown")
Dim activeTable As String
activeTable = ActiveSheet.ListObjects(1).Name
ActiveSheet.ListObjects(1).Range.Activate
Selection.ListObject.ListColumns.Add Position:=4
Range("D1") = "CaseLink"
Range("D2").Select
If Range("A2").Value = "CSv2" Then _
ActiveCell.FormulaR1C1 = _
"=HYPERLINK(""https://open.companytest.com/fredsfakeurl.aspx?conv=""&[#[case_number]]&""&st="",[#[case_number]])"
Else
ActiveCell.FormulaR1C1 = _
"=HYPERLINK(""https://open.companytest.com/janesfakeurl.aspx?rdx=9992956J43211&help=""&[#[case_number]]&""&st="",[#[case_number]])"
Range("A1").Select
End If
Cells.Select
Cells.EntireColumn.AutoFit
End Sub
Try this. You'll need to adjust the column header for the first column (CSv1/2)
Sub InsertHyperlink_EscFeedback()
Const LINK1 = "HYPERLINK(""https://open.companytest.com/fredsfakeurl.aspx?conv=""&[#[case_number]]&""&st="",[#[case_number]])"
Const LINK2 = "HYPERLINK(""https://open.companytest.com/janesfakeurl.aspx?rdx=9992956J43211&help=""&[#[case_number]]&""&st="",[#[case_number]])"
Dim ws As Worksheet, lo As ListObject, lc As ListColumn
Set ws = ActiveSheet
Set lo = ws.ListObjects(1)
lo.Name = "Drilldown"
Set lc = lo.ListColumns.Add(Position:=4)
lc.Name = "CaseLink"
lc.DataBodyRange.Formula = "=IF([#[link_type]]=""CSv2""," & LINK1 & "," & LINK2 & ")"
lo.Range.EntireColumn.AutoFit
End Sub
As there were only two values possible in Column A, I was able to resolve with a simple single line IF statement:
ActiveCell.FormulaR1C1 = _
"=IF(RC[-3]=""CSv1"",HYPERLINK(CONCATENATE(""https://open.companytest.com/fredsfakeurl.aspx?rdx=9992956J43211&conv="",RC[-2]),RC[-2]),HYPERLINK(CONCATENATE(""https://open.companytest.com/janesfakeurl.aspx?conv="",RC[-2]),RC[-2]))"

multiple sheet lookup in excel using VBA

I am trying to write a VBA code to do lookup in excel.
What I have is two worksheets and want to lookup between these two sheets and lookup result should be present in third sheet. How will be able to do this in VBA.
SHEET1:
CHANGE NUMBER |DATE |
---------------|--------|
1555081 |5/3/2018|
1555083 |5/3/2018|
1555089 |5/3/2018|
1555327 |5/3/2018|
1555381 |5/3/2018|
1555526 |5/3/2018|
SHEET2:
TICKET NO |CLIENT REFERENCE ID|
--------------|-------------------|
T20161103.0040|1555081 |
T20170113.0057|1555526 |
T20170113.0064|1555589 |
T20170125.0035|1555083 |
T20170130.0091|1555526 |
T20170130.0092| |
T20170208.0073| |
My lookup formula will be
= vlookup(sheet1!A1,sheet2!B:B,1,FALSE)
How can I do that in VBA. Any help will be appreciated
Thanks
Abdulquadir
Try this for a start
My lookup formula will be
= vlookup(sheet1!A1,sheet2!B:B,1,FALSE)
.
Sub randomstackmacro()
Dim output As String
Dim Sheet1 As Worksheet
Dim Sheet2 As Worksheet
'This will apply the vba on cell C2 // Please modify this according to your requirements>
Range("C2").Select
Set Sheet1 = ThisWorkbook.Sheets("Sheet1")
Set Sheet2 = ThisWorkbook.Sheets("Sheet2")
output = Application.WorksheetFunction.VLookup(Sheet1.Range("A3"), Sheet2.Range("A:B"), 2, False)
'Output of Vlook up is set to the active cell as an example
ActiveCell.Formula = output
End Sub
OUTPUT
Sub lookuptest()
Worksheets("CA").Range("A:A").Copy Worksheets("OUTPUT").Range("A:A")
On Error Resume Next
Dim cn_Row As Long
Dim cn_Clm As Long
Dim sheet1 As Worksheet
Dim sheet2 As Worksheet
Dim sheet3 As Worksheet
Set sheet1 = ThisWorkbook.Sheets("CA")
Set sheet2 = ThisWorkbook.Sheets("AT")
Set sheet3 = ThisWorkbook.Sheets("OUTPUT")
With sheet1
sourcelastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
'MsgBox "source file last row is: " & sourcelastrow
End With
Table1 = sheet1.Range("$A$2:$A$" & sourcelastrow) ' Employee_ID Column from Employee table
Table2 = sheet2.Range("B:B") ' Range of Employee Table 1
cn_Row = sheet3.Range("B2").Row ' Change E3 with the cell from where you need to start populating the Department
cn_Clm = sheet3.Range("B2").Column
For Each cl In Table1
sheet3.Cells(cn_Row, cn_Clm) = Application.WorksheetFunction.VLookup(cl, Table2, 1, False)
cn_Row = cn_Row + 1
Next cl
'MsgBox "Done Lookup with Change Number"
Dim id_row As Long
Dim id_clm As Long
Table3 = sheet1.Range("A:B")
id_row = sheet3.Range("C2").Row
id_clm = sheet3.Range("C2").Column
For Each cl In Table1
sheet3.Cells(id_row, id_clm) = Application.WorksheetFunction.VLookup(cl, Table3, 2, False)
id_row = id_row + 1
Next cl
MsgBox "Done"
End Sub

Copy and paste columns between sheets based on an Excel "Map" or table? - Further autoexecute file links?

I have been doing this manually for far too long, and I feel there must be a way to speed up this process. Hopefully you guys can help me.
Currently I have an excel file that has written in VBA macros that copy and paste certain columns from one sheet and paste them into another sheet. There are about 160 of these written, each with about 10 copy/paste commands. (This workbook is called workbook A)
Currently my method involves me opening up Workbook B, copying the data into a sheet in Workbook A, selecting a macro to run from a dropdown in Workbook A, copying the results and pasting them into a "master" Workbook C. The problem for me is that There are frequent instances when maps - that is the column locations of data - change in Workbook B. I maintain a "master map" file that looks something like this:
Contract# | Purchaser | Price | Quantity | Total
------------------------------------------------
A | B | C | D | E
------------------------------------------------
G | D | C | A | B
------------------------------------------------
etc. (Apologies if that's messy)
What I would like to do is have the sheet do the copying and pasting automatically based on A: The column on the map (the pasting column) and B: the letter represented in the row for that particular Contract (this copies the column of which the letter represented).
Is this possible?
Secondly, if that is - The option to have this execute automatically by specifying the file patch of each file would be outstanding (I have a clear taxonomy of file locations and names). Is that also possible?
Added condensed sample of ran macro.
The macro is quite simple, here is a sample...
Sub PA979()
Application.ScreenUpdating = False
'Retail $
Sheets("VSR Input").Select
Range("x1:x5004").Copy
Sheets("Sheet1").Select
Range("q4").Select
ActiveSheet.Paste
'PA $
Sheets("VSR Input").Select
Range("y1:y5004").Copy
Sheets("Sheet1").Select
Range("s4").Select
ActiveSheet.Paste
'Q
Sheets("VSR Input").Select
Range("z1:z5004").Copy
Sheets("Sheet1").Select
Range("t4").Select
ActiveSheet.Paste
'Total $
Sheets("VSR Input").Select
Range("aa1:aa5004").Copy
Sheets("Sheet1").Select
Range("u4").Select
ActiveSheet.Paste
Range("A1").Select
Dim ws As Worksheet
Set ws = Worksheets("Sheet1")
Dim usedRows As Long
usedRows = ws.Cells(ws.Rows.Count, "U").End(xlUp).Row
Application.ScreenUpdating = False
Sheets("Sheet1").Select
Range("v3").Select
ActiveCell.FormulaR1C1 = "PA#"
ActiveCell.Offset(1, 0).Range("A1").Select
ActiveCell = "979"
ActiveCell.Select
Selection.AutoFill Destination:=ActiveCell.Range(Cells(1, 1), Cells(usedRows - 3, 1)), Type:= _
xlFillDefault
Range("v4").Select
Selection.Copy
Range(Selection, Selection.End(xlDown)).Select
ActiveSheet.Paste
ActiveSheet.Range("A1").Select
End Sub `
Assumes the workbook containing the macro has a worksheet "map" with one row for each contract number:
A: contract number
B: file path for the input workbook
C-F: source column letters for each of the columns being copied
Row 2 on the Map sheet has the destination column letters in cols C-F
Compiled but not tested:
Option Explicit
Sub Tester()
CopyData 979
End Sub
Sub CopyData(contractNumber)
Dim wbInput As Workbook
Dim wbDest As Workbook
Dim shtIn As Worksheet, shtDest As Worksheet, shtMap As Worksheet
Dim usedRows As Long
Dim arrDestCols, x As Integer, cFrom, cTo
Dim f As Range, mapRow As Range
'has the column mapping info for each contract number
Set shtMap = ThisWorkbook.Sheets("Map")
'find the row for this contract number
Set f = shtMap.Range("A3:A100").Find(contractNumber, , xlValues, xlWhole)
If f Is Nothing Then
MsgBox "contract number " & contractNumber & " not found!"
Exit Sub
Else
Set mapRow = f.EntireRow
End If
'assumes input file path is in column B
Set wbInput = Workbooks.Open(mapRow.Cells(2).Value)
Set shtIn = wbInput.Sheets("VSR Input")
Set wbDest = ThisWorkbook
Set shtDest = wbDest.Sheets("Sheet1")
Application.ScreenUpdating = False
For x = 1 To 4
' "source" column letters are in columns C-F of the found row
cFrom = mapRow.Cells(2 + x).Value
' "destination" column letters are in C2:F2 of the Map sheet
cTo = shtMap.Rows(2).Cells(2 + x).Value
shtIn.Range(cFrom & "1").Resize(5004, 1).Copy shtDest.Range(cTo & "4")
Next x
With shtDest
usedRows = .Cells(.Rows.Count, "U").End(xlUp).Row
.Range("v3").Value = contractNumber
.Range("v4").Resize(usedRows - 3, 1).Value = contractNumber
End With
wbInput.Close False
End Sub

range matrix A1:Q38 from 1400 files & listing as single column in new workbook row A1 followed by row A2 etc

1400 separate excell files need to take all data from each file row by row from range A1:Q38. need that data in a single column list in order of each original row. Need to read row A1:Q1 and write to A1:A17 then read A2:Q2 and write to A18:A34 continue until each file is read. Need data in a new workbook. Would like to ignore writing empty cells to new list if possible. following code will do job in one worksheet. Need it to work against 1400 files programmatically.
Sub readvalues()
row2 = 1
For Row = 1 To 38
For col = 1 To 17
READCELL = Worksheets("sheet1").Cells(Row, col).Value
Worksheets("sheet2").Cells(row2, 1) = READCELL
row2 = row2 + 1
Next col
Next Row
End Sub
What you need to use is called Transpose. PasteSpecial allows you to paste using transposed cells. (Rows become columns and columns become rows, as in a Matrix)
Dim targetWorkbook As Workbook
Dim rowCount As Integer
rowCount = 1
Set targetWorkbook = Workbooks.Add
Application.DisplayAlerts = False
targetWorkbook.SaveAs Filename:="C:/MyData/TransposedRow" & rowCount
ActiveSheet.UsedRange.Cells.Copy
targetWorkbook.Sheets(1).Cells.PasteSpecial Transpose:=True
Application.CutCopyMode = False
rowCount = rowCount + 1

What's the best way to compare two sheets in an Excel workbook

Given I have the following
<Sheet 1>
Item QTY
A 5
B 1
C 3
<Sheet 2>
Item QTY
A 15
B 4
C 1
D 8
What is the best way to generate a report showing the difference between sheet 1 and 2?
Like
<Difference>
Item QTY
A 10
B 3
C -2
D 8
You shouldn't need VBA for this.
Here's what you do:
Create a new worksheet (Sheet3).
Set it up to look like this:
alt text http://img16.imageshack.us/img16/2451/consolidationsheet.jpg
Here are the formulas you will need (paste each one into the proper cell):
Note: the first two are "array formulas" -- after you paste in the formula, double-click the cell and do Ctrl-Shift-Enter (braces {} should appear around the formula)
------------------------------------------------------------------------------
Cell Formula
------------------------------------------------------------------------------
B2 =SUM(IF(Sheet1!A:A="",0,1)) <-- array formula: use Ctrl-Shift-Enter instead of Enter
B3 =SUM(IF(Sheet2!A:A="",0,1)) <-- array formula: use Ctrl-Shift-Enter instead of Enter
D2 =IF(D1=D$1,2,IF(OR(D1=B$2,D1=""),"",D1+1))
E2 =IF(D2="",IF(D1="",IF(OR(E1=B$3,E1=""),"",E1+1),2),"")
G2 =IF(D2<>"",INDEX(Sheet1!A:A,D2),IF(E2<>"",INDEX(Sheet2!A:A,E2),""))
H2 =IF(D2<>"",-INDEX(Sheet1!B:B,D2),IF(E2<>"",INDEX(Sheet2!B:B,E2),""))
Drag the formulas in D2:H2 down as far as you need to cover all the data for sheets 1 and 2.
Select all the data in columns G & H (including the headings).
Do Insert > PivotTable and click OK.
Click the Pivot Table and drag []Item to the Row Labels box and []QTY to the Values box.
That's it. The Pivot Table will contain a summary for each item. No item will be repeated, and no item will be left out. The "Sum of QTY" column will actually contain the difference (since the formula uses negative for all sheet 1 quantities).
In Excel VBA, use a Dictionary. Use your items from one of the sheets as keys, QTY as values. Put the item/QTY pairs of sheet 1 into the dictionary, then run through the items of sheet 2 update the dictionary accordingly to get the differences in there. Finally, put the result into sheet 3.
EDIT: here is a complete example in code (you have to set a reference to the Microsoft Scripting runtime to get it working this way):
Option Explicit
Sub CreateDiff()
Dim dict As New Dictionary
Dim sh1 As Worksheet, sh2 As Worksheet, sh3 As Worksheet
Dim i As Long, v As String
Set sh1 = ThisWorkbook.Sheets("Sheet1")
Set sh2 = ThisWorkbook.Sheets("Sheet2")
Set sh3 = ThisWorkbook.Sheets("Sheet3")
For i = 2 To sh1.Cells.SpecialCells(xlCellTypeLastCell).Row
v = Trim(sh1.Cells(i, 1).Value)
dict(v) = -sh1.Cells(i, 2).Value
Next
For i = 2 To sh2.Cells.SpecialCells(xlCellTypeLastCell).Row
v = Trim(sh2.Cells(i, 1).Value)
If dict.Exists(v) Then
dict(v) = dict(v) + sh2.Cells(i, 2).Value
Else
dict(v) = sh2.Cells(i, 2).Value
End If
Next
For i = 0 To dict.Count - 1
v = dict.Keys(i)
sh3.Cells(i + 2, 1) = v
sh3.Cells(i + 2, 2) = dict(v)
Next
End Sub
you could merge both sets of data onto a single sheet side-by-side (item1, qty, item2, qty) then use the VLOOKUP() excel function to find the data from the opposite set.
One possibility is to use ADO
Dim cn As Object
Dim rs As Object
Dim strFile As String
Dim strCon As String
Dim strSQL As String
''http://support.microsoft.com/kb/246335
strFile = Workbooks("Book1.xls").FullName
''Note HDR=Yes, the names in the first row of the range
''can be used.
strCon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & strFile _
& ";Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1"";"
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
cn.Open strCon
strSQL = "SELECT s2.Item, s2.Qty-IIf(s1.Qty Is Null,0,s1.Qty) FROM [Sheet2$] s2 " _
& "LEFT JOIN [Sheet1$] s1 ON s2.Item=s1.Item"
rs.Open strSQL, cn, 3, 3
Workbooks("Book1.xls").Worksheets("Sheet3").Cells(2, 1).CopyFromRecordset rs
Why use VBA?
On Sheet 3 comparison sheet list all possible items from sheets 1 and 2 in column A then in Column B use the following formula. Starting in B2 then copy down.
=if(iserror(vlookup(A2,Sheet2'$A$2:$B$5,2,false),0,vlookup(A2,Sheet2'$A$2:$B$5,2,false))-if(iserror(vlookup(A2,Sheet1'$A$2:$B$5,2,false),0,vlookup(A2,Sheet1'$A$2:$B$5,2,false))
Change the table range as necessary.

Resources