Create a summary sheet based on multiple worksheets - vba

I have number of worksheets containing the same structure and same number of rows. Now I would like to create a master sheet to have an overview of all the worksheets using VBA.
It is like a balance sheet showing the performance over several years, which Years are on the headings and items are on rows.
Now the yearly data are put on multiple worksheets named "2012", "2013" and "2014".
Column B on sheet 1 ("2012") will be copied onto col B on "master" but for the following sheets ("2013", "2014"), data will be placed onto the next column on "master" (ie 2013 data on col C, 2014 data on col D).
I would like to have a workable macro which can count numbers of worksheets and copy paste specific data on a right column of master sheet.

1) create in a sheet a table like that: where the first column is where the destination cell in your Master report. And the 2nd column is where the data will be copied.
2) Put all your Worksheets in a folder
3) Run this macro, which need to be placed in Master module
Option Explicit
'assuming that:
'- "Excel Column Code" is in column A
'- "Form Cell Code" is in column B
'in zmaster.xlsm!Sheet2
Sub UpdateData()
Dim sFile As String, sPath As String
Dim srcWbk As Workbook, dstWbk As Workbook
Dim srcWsh As Worksheet, dstWsh As Worksheet, infoWsh As Worksheet
Dim i As Long, j As Long, k As Long
On Error GoTo Err_UpdateData
Set dstWbk = ThisWorkbook
Set dstWsh = dstWbk.Worksheets("Sheet1")
Set infoWsh = dstWbk.Worksheets("Sheet2")
sPath = "C:\Desktop\New folder\"
sFile = Dir(sPath)
Do While Len(sFile) > 0
If sFile = "zmaster.xlsm" Then
GoTo SkipNext
End If
Set srcWbk = Workbooks.Open(sPath & sFile)
Set srcWsh = srcWbk.Worksheets(1)
i = 2
'loop through the information about copy-paste method
Do While infoWsh.Range("A" & i) <> ""
'get first empty row, use "Excel Column Code" to get column name
j = GetFirstEmpty(dstWsh, infoWsh.Range("A" & i))
'copy data from source sheet to the destination sheet
'use "Form Cell Code" to define destination cell
srcWsh.Range(infoWsh.Range("B" & i)).Copy dstWsh.Range(infoWsh.Range("A" & i) & j)
i = i + 1
Loop
srcwbk.Close SaveChanges:=False
SkipNext:
sFile = Dir
Loop
Exit_UpdateData:
On Error Resume Next
Set srcWsh = Nothing
Set dstWsh = Nothing
Set srcWbk = Nothing
Set dstWbk = Nothing
Exit Sub
Err_UpdateData:
MsgBox Err.Description, vbExclamation, Err.Number
Resume Exit_UpdateData
End Sub
'returns first empty row in a destination sheet based on column name
Function GetFirstEmpty(ByVal wsh As Worksheet, Optional ByVal sCol As String = "A") As Long
GetFirstEmpty = wsh.Range(sCol & wsh.Rows.Count).End(xlUp).Row + 1
End Function

Related

Finding the address of a table range using vba

I am working with an excel sheet that has a bunch of sheets with data in tables. I am trying to consolidate the sheets. I do not want the copied data to be in tables. I am able to find the tables range address for all the sheets except one, which is retunring an address of $1:$104. All the other ranged are like this "$A$1:$J$43" . When I try to copy this table using the address it returns , I get the runtime error "1004". For now , the code rewrite all the tables in the same place, but I will be changing the code to copy the tables into different places in the destination sheet.
Here is my code:
Sub mergeWorksheets()
Dim wrk As Workbook 'Workbook object - Always good to work with
object variables
Dim sht As Worksheet 'Object for handling worksheets in loop
Dim trg As Worksheet 'Master Worksheet
Dim rng As Range 'Range object
Dim colCount As Integer 'Column count in tables in the worksheets
Dim mLastRow As Integer
Dim LastRow As Integer
Dim rngFound As Range
Dim i As Integer
Set wrk = ActiveWorkbook 'Working in active workbook
'We don't want screen updating
Application.ScreenUpdating = False
' would rather not do a loop but using a function to check and delete sheet renders error
For Each Sheet In ActiveWorkbook.Worksheets
If Sheet.Name = "Master" Then
Application.DisplayAlerts = False
Sheets("Master").Delete
Application.DisplayAlerts = True
End If
Next Sheet
' Add new worksheet as the last worksheet
Set trg = wrk.Worksheets.Add(Before:=wrk.Worksheets(1))
' Rename the new worksheet
trg.Name = "Master"
'We can start loop
For Each sht In wrk.Worksheets
'If worksheet in loop is the last one, stop execution (it is Master worksheet)
If sht.Name Like "*Attri*" Then
Debug.Print sht.Name
'Find the last row of the master sheet
Set rngFound = trg.UsedRange.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious)
If Not rngFound Is Nothing Then
'you found the value - do something
mLastRow = rngFound.Row
Debug.Print "Last row of master " & rngFound.Address, mLastRow
Else
' you didn't find anything becasue sheet is empty - first pass
mLastRow = 0
End If
For Each tbl In sht.ListObjects
'Do something to all the tables...
Debug.Print tbl.Name
Debug.Print tbl.Range.Address
'Put data into the Master worksheet
tbl.Range.Copy Destination:=trg.Range("B1")
Next tbl
' trg.Cells(mLastRow + 1, 1).Value = "Tab Name"
' trg.Cells(mLastRow + 1, 1).Font.Bold = "True"
' trg.Range("A" & mLastRow + 1).Value = sht.Name
Debug.Print "-------"
Else
' Debug.Print "error " & sht.Name & " is missing header "
End If
Next sht
That funny range is obviously there. What you can do is to control the size of the data to be copied. If you can set a meaningful maximum value for table width then you can limit size like this:
const MAXWID = 1000
Dim r As Range
If tbl.Range.Columns.Count > MAXWID Then
Set r = tbl.Range.Resize(, MAXWID)
Else
Set r = tbl.Range
End If
r.Copy Destination:=trg.Range("B1")
Funny things can happen to the height of the table(s), too, so you may want to implement this for the other dimension. For appending the tables you need to know where the first empty row is:
FirstEmptyRow = trg.Range("B1").SpecialCells(xlCellTypeLastCell).Row + 1
r.Copy Destination:=trg.Cells(FirstEmptyRow, "B")
For sheet manipulation you need to use On Error ... like this:
Application.DisplayAlerts = False
On Error Resume Next
Set trg = wrk.Sheets("Master")
If Err.Number = 0 Then ' sheet exists
trg.Usedrange.Delete ' delete all existing data -> have a clean sheet
Else ' sheet doesn't exist, Add new worksheet as the first worksheet
Set trg = wrk.Worksheets.Add(Before:=wrk.Worksheets(1))
If Err.Number <> 0 Then < sheet is not added, handle error...>
trg.Name = "Master"
End If
On Error Goto 0
Application.DisplayAlerts = True
It's worth taking the time to learn how error handling works in VBA.
And finally: use Option Explicit. It pays.

VBA Excel Copy and Paste a table into a new Workbook and choice which Columns i want to copy

I want to copy a table into a new Workbook while choosing which range I want to copy and knowing that the first Columns ("A") is automatically copied. (rows are not a problem, all of them have to be copied)
For example, i have a table composed of 28 rows and 10 columns. Added to A1:A28 (first columns, all rows),i want just to copy the column 5 and 8 with all its rows.
That's what i have until now but it doesn't work.
Sub CommandButton1_Click()
Dim newWB As Workbook, currentWB As Workbook
Dim newS As Worksheet, currentS As Worksheet
Dim CurrCols As Variant
Dim rng As rang
'Copy the data you need
Set currentWB = ThisWorkbook
Set currentS = currentWB.Sheets("Feuil1")
'select which columns you want to copy
CurrCols = InputBox("Select which column you want to copy from table (up to 10)")
If Not IsNumeric(CurrCols) Then
MsgBox "Please select a valid Numeric value !", vbCritical
End
Else
CurrCols = CLng(CurrCols)
End If
'Set rng = currentWB.currentS.Range(Cells(1, A), Cells(27, CurrCols)).Select
currentS.Range("A1:A27").Select
Selection.copy
Set rng = currentWB.currentS.Range(Cells(1, CurrCols), Cells(28, CurrCols)).Select
rng.copy
'Create a new file that will receive the data
Set newWB = Workbooks.Add
With newWB
Set newS = newWB.Sheets("Feuil1")
newS.Range("A1").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End With
End Sub
Can you help please solving it? Thanks in advance!
You can't copy a non-continuous range but you can load the data into an array and write it once to the new workbook.
Private Sub CommandButton1_Click()
Dim arData
Dim MyColumns As Range, Column As Range
Dim x As Long, y As Long
On Error Resume Next
Set MyColumns = Application.InputBox(Prompt:="Hold down [Ctrl] and click the columns to copy", Title:="Copy Columns to new Workbook", Type:=8)
On Error GoTo 0
If MyColumns Is Nothing Then Exit Sub
Set MyColumns = Union(Columns("A"), MyColumns.EntireColumn)
Set MyColumns = Intersect(MyColumns, ActiveSheet.UsedRange)
ReDim arData(1 To MyColumns.Rows.Count, 1 To 1)
For Each Column In MyColumns.Columns
y = y + 1
If y > 1 Then ReDim Preserve arData(1 To MyColumns.Rows.Count, 1 To y)
For x = 1 To Column.Rows.Count
arData(x, y) = Column.Rows(x)
Next
Next
With Workbooks.Add().Worksheets(1)
.Range("A1").Resize(UBound(arData, 1), UBound(arData, 2)) = arData
.Columns.AutoFit
End With
End Sub
try this (commented) code
Option Explicit
Sub CommandButton1_Click()
Dim newSht As Worksheet
Dim currCols As String
Dim area As Range
Dim iArea As Long
Set newSht = Workbooks.add.Worksheets("Feuil1") '<--| add a new workbook and set its "Feuil1" worksheet as 'newSht'
currCols = Replace(Application.InputBox("Select which column you want to copy from table (up to 10)", "Copy Columns", "A,B,F", , , , , 2), " ", "") '<--| get columns list
With ThisWorkbook.Worksheets("Feuil1") '<--| reference worksheet "Feuil1" in the workbook this macro resides in
For Each area In Intersect(.Range(ColumnsAddress(currCols)), .Range("A1:G28")).Areas ' loop through referenced worksheet areas of the range obtained by crossing its listed columns with its range "A1:G28"
With area '<--| reference current area
newSht.Range("A1").Offset(, iArea).Resize(.Rows.Count, .Columns.Count).value = .value '<--| copy its values in 'newSht' current column offset from "A1" cell
iArea = iArea + .Columns.Count '<--| update current column offset from 'newSht' worksheet "A1" cell
End With
Next area
End With
End Sub
Function ColumnsAddress(strng As String) As String
Dim elem As Variant
For Each elem In Split(strng, ",")
ColumnsAddress = ColumnsAddress & elem & ":" & elem & ","
Next
ColumnsAddress = Left(ColumnsAddress, Len(ColumnsAddress) - 1)
End Function
I think you can copy all column to a temp sheet and then write some code to delete the useless column. finally paste the table to your expected area.

VBA - copy data from one worksheet t

Good morning,
I'm attempting to copy data from multiple worksheets (in cells M78:078) into one, where the name in the column (L) of the summary sheet matches to the worksheet name (pasting into columns Z:AA in the summary sheet.
At present the below code is erroring out:
Sub Output_data()
Application.ScreenUpdating = False
For Each ws In ActiveWorkbook.Worksheets
If ActiveSheet.Range("L:L").Value = wkSht.Name Then
ws.Range("M78:O78").Copy
ActiveSheet.Range("L").CurrentRegion.Copy Destination:=wkSht.Range("Z:AA").Paste
End If
Next ws
Application.ScreenUpdating = True
End Sub
Any help would be great.
DRod
Something like this should work for you. I commented the code in an attempt to explain what it does.
Sub Output_data()
Dim wb As Workbook
Dim ws As Worksheet
Dim wsGet As Worksheet
Dim LCell As Range
Dim sDataCol As String
Dim lHeaderRow As Long
sDataCol = "L" 'Change to be the column you want to match sheet names agains
lHeaderRow = 1 'Change to be what your actual header row is
Set wb = ActiveWorkbook
Set ws = wb.Sheets("Summary") 'Change this to be your Summary sheet
'Check for values in sDataCol
With ws.Range(sDataCol & lHeaderRow + 1, ws.Cells(ws.Rows.Count, sDataCol).End(xlUp))
If .Row <= lHeaderRow Then Exit Sub 'No data
'Loop through sDataCol values
For Each LCell In .Cells
'Check if sheet named that value exists
If Evaluate("ISREF('" & LCell.Text & "'!A1)") Then
'Found a matching sheet, copy M78:O78 to the corresponding row, column Z and on
Set wsGet = wb.Sheets(LCell.Text)
wsGet.Range("M78:O78").Copy ws.Cells(LCell.Row, "Z")
End If
Next LCell
End With
End Sub

How to run a macro on some but not all sheets in a workbook?

I have a workbook that contains worksheets for each industry group in the S&P 500 and wrote the macro below to update all the stock information on them when I press a command button on the first worksheet. The macro works perfectly, but when I go to add additional sheets that I do not want to update with this macro it stops working. I tried using the "If Not" statements below, but it did not seem to work.
Sub Get_Stock_Quotes_from_Yahoo_Finance_API()
'Run the API for every sheet in the workbook
Dim Sht As Worksheet
For Each Sht In ThisWorkbook.Worksheets
'Look to see what the sheet is named and run the macro if it is not what is below
If Not Sht.Name = "Cover" _
And Not Sht.Name = "Select Industry" Then
Sht.Activate
' Dim varibales and set range
Dim head As Range
Set head = Worksheet.Range("A2")
'dim variables
Dim I As Integer
Dim Symbols As String: Symbols = ""
Dim SpecialTags As String: SpecialTags = ""
Dim Yahoo_Finance_URL As String: Yahoo_Finance_URL = "http://finance.yahoo.com/d/quotes.csv?s="
Dim rng As Range
Dim cell As Range
' Get the Stock Symbols
Set rng = Range(head.Offset(1, 0), head.Offset(1, 0).End(xlDown))
For Each cell In rng ' Starting from a cell below the head cell till the last filled cell
Symbols = Symbols & cell.Value & "+"
Next cell
Symbols = Left(Symbols, Len(Symbols) - 1) ' Remove the last '+'
' Get the Special Tags
Set rng = Range(head.Offset(0, 1), head.Offset(0, 1).End(xlToRight))
For Each cell In rng ' Starting from a cell to the right of the head cell till the last filled cell
SpecialTags = SpecialTags & cell.Value
Next
' Put the desciption/name of each tag in the cell above it
Dim SpecialTagsArr() As String: Dim TagNamesArr() As String
Call Get_Special_Tags(SpecialTagsArr, TagNamesArr)
For Each cell In rng
cell.Offset(-1, 0).Value = FindTagName(cell.Value, SpecialTagsArr, TagNamesArr)
Next
Yahoo_Finance_URL = Yahoo_Finance_URL & Symbols & "&f=" & SpecialTags
Call Print_CSV(Yahoo_Finance_URL, head)
Next Sht
'At the end of the program say it has all been updated
MsgBox ("All Data Updated")
End Sub
Change
If Not Sht.Name = "Cover" _
And Not Sht.Name = "Select Industry" Then
To
If Sht.Name <> "Cover" And Sht.Name <> "Select Industry" Then
Don't forget your End If before Next Sht
Refering to Kevin's second code - now the exclusion logic is flawed. I suggest the following:
Function IsIn(element, arr) As Boolean
IsIn = False
For Each x In arr
If element = x Then
IsIn = True
Exit Function
End If
Next x
End Function
Sub Get_Stock_Quotes_from_Yahoo_Finance_API()
Dim skippedSheets()
skippedSheets = Array("Cover,Select Industry,bla bla")
For Each Sh In ActiveWorkbook.Worksheets
If Not IsIn(Sh.Name, skippedSheets) Then
' ... process Sh
End If
Next Sh
End Sub
Now you have all sheet names which are to be excluded in one place (the array assignment) and the inner code block will only be executed if the current sheet name is not element of that array.
Second source of error: you already started qualifying the ranges (like in Set head = Sht.Range("A2")). Do the same in 2 other places, with
Set rng = Sht.Range(head.Offset(1, 0), head.Offset(1, 0).End(xlDown))
and
Set rng = Sht.Range(head.Offset(0, 1), head.Offset(0, 1).End(xlToRight))
Last, you don't have to activate a sheet. You work with the Sht object and qualified ranges.
Dim I as Integer is unused.

Match Columns on two excel worksheets and copy data

I have two data sheets within the same excel file:
Sheet1 as "Data" with 7 columns:
The second sheet is "Main" with 5 columns:
The same column to match the two files is "name". I want to have a VBA code that matches the name on both sheet and copy data from proc1 - Proc4 from sheet "Main" to sheet "data" by matching the column names on both sheets.
I searched stack overflow for similar question and here is the code that I found (modified it slightly):
Sub CopyData()
Dim shtImport As Worksheet
Dim shtMain As Worksheet
Set shtImport = ThisWorkbook.Sheets("Data")
Set shtMain = ThisWorkbook.Sheets("Main")
Dim CopyColumn As Long
Dim CopyRow As Long
Dim LastColumn As Long
'- for each column in row 1 of import sheet
For CopyColumn = 1 To shtImport.Cells(1, shtImport.Columns.Count).End(xlToRight).Column
'- check what the last column is with data in column
LastRowOfColumn = shtImport.Cells(shtImport.Columns.Count, CopyColumn).End(xlToRight).Column
'if last column was larger than one then we will loop through rows and copy
If LastColumn > 1 Then
For CopyRow = 1 To LastColumn
'- note we are copying to the corresponding cell address, this can be modified.
shtMain.Cells(CopyRow, CopyColumn).value = shtImport.Cells(CopyRow, CopyColumn).value
Next CopyRow
End If
Next CopyColumn
End Sub
This is not working the way I want it to work. Can somebody please help me with this problem. Thanks a lot!
Try this code:
Sub CopyData()
Dim shtImport As Worksheet
Dim shtMain As Worksheet
Set shtImport = ThisWorkbook.Sheets("Data")
Set shtMain = ThisWorkbook.Sheets("Main")
'From Main to Data
Dim rngImpTitles As Range
Set rngImpTitles = shtImport.Rows(1)
Dim rngImpNames As Range
Set rngImpNames = shtImport.Columns(1)
Dim CopyColumn As Long
Dim CopyRow As Long
Dim foundRow As Long
Dim foundCol As Long
On Error Resume Next
'for each column in row 1 of import sheet
For CopyColumn = 2 To shtMain.Cells(1, shtMain.Columns.Count).End(xlToLeft).Column
foundCol = rngImpTitles.Find(shtMain.Cells(1, CopyColumn).Value2).Column
If Err.Number <> 0 Then
MsgBox "Not such a col title in importsheet for " & vbNewLine & _
shtMain.Cells(1, CopyColumn)
Err.Clear
GoTo skip_title
End If
For CopyRow = 2 To shtMain.Cells(shtMain.Rows.Count, 1).End(xlUp).Row
foundRow = rngImpNames.Find(shtMain.Cells(CopyRow, 1)).Row
If Err.Number <> 0 Then
MsgBox "Not such a row name in importsheet for " & vbNewLine & _
shtMain.Cells(CopyRow, 1)
Err.Clear
GoTo skip_row
End If
If Len(shtMain.Cells(CopyRow, CopyColumn)) <> 0 Then
shtMain.Cells(CopyRow, CopyColumn).Copy shtImport.Cells(foundRow, foundCol)
End If
skip_row:
Next CopyRow
skip_title:
Next CopyColumn
End Sub