I have a workbook with 147 tables across different worksheets (most worksheets have multiple tables and new tables are added regularly).
I need to
create connection-only queries for each of the tables,
edit them (Remove all Columns except two, transpose the remaining two columns and then turn the first row into Column headers) and
name them in a particular pattern e.g. ConnectionTable1 and so on.
This process needs to be followed for each new table added to the workbook.
I would like to automate this using VBA. So far I have the following code (found from searching on the web) which creates connection-only queries for all of the tables in the workbook and adds the data to the Data Model.
Code:
Sub CreateConnectionToAllTables()
‘Creates Connection Only Queries to all tables in the active workbook.
Dim wb As Workbook
Dim ws As Worksheet
Dim lo As ListObject
Dim sName As String
Dim sFormula As String
Dim wq As WorkbookQuery
Dim bExists As Boolean
Dim vbAnswer As VbMsgBoxResult
Dim vbDataModel As VbMsgBoxResult
Dim i As Long
Dim dStart As Double
Dim dTime As Double
‘Display message box to prompt user to run the macro
vbAnswer = MsgBox(“Do you want to run the macro to create connections for all Tables in this workbook?”, vbYesNo, “Power Query Connect All Tables Macro”)
If vbAnswer = vbYes Then
‘Prompt user for Data Model option
vbDataModel = MsgBox(“Do you want to add the data to the Data Model?”, vbYesNo + vbDefaultButton2, “Power Query Connect All Tables Macro”)
‘Set variables
dStart = Timer
Set wb = ActiveWorkbook
‘Loop sheets and tables
For Each ws In ActiveWorkbook.Worksheets
For Each lo In ws.ListObjects
sName = lo.Name
sFormula = “Excel.CurrentWorkbook(){[Name=””” & sName & “””]}[Content]”
‘Check if query exists
bExists = False
For Each wq In wb.Queries
If InStr(1, wq.Formula, sFormula) > 0 Then
bExists = True
End If
Next wq
‘Add query if it does not exist
If bExists = False Then
‘Add query
wb.Queries.Add Name:=sName, _
Formula:=”let” & Chr(13) & “” & Chr(10) & ” Source = Excel.CurrentWorkbook(){[Name=””” & sName & “””]}[Content]” & Chr(13) & “” & Chr(10) & “in” & Chr(13) & “” & Chr(10) & ” Source”
‘Add to Data Model
If vbDataModel = vbYes Then
wb.Connections.Add2 Name:=”Query – ” & sName, _
Description:=”Connection to the ‘” & sName & “‘ query in the workbook.”, _
ConnectionString:=”OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=” & sName & “;Extended Properties=”, _
CommandText:=”” & sName & “”, _
lCmdtype:=6, _
CreateModelConnection:=True, _
ImportRelationships:=False
‘Add connection only
Else
wb.Connections.Add2 Name:=”Query – ” & sName, _
Description:=”Connection to the ‘” & sName & “‘ query in the workbook.”, _
ConnectionString:=”OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=” & sName & “;Extended Properties=”””””, _
CommandText:=”SELECT * FROM [” & sName & “]”, _
lCmdtype:=2, _
CreateModelConnection:=False, _
ImportRelationships:=False
End If
‘Count connections
i = i + 1
End If
Next lo
Next ws
‘Calc run time
dTime = Timer – dStart
MsgBox i & ” connections have been created in ” & Format(dTime, “0.0”) & ” seconds.”, vbOKOnly, “Process Complete”
End If
End Sub
The above code works fine. But I need help modifying it to perform steps (2) editing the queries and (3) naming them in a particular pattern slightly different from the name of the table they are connected to.
Disclaimer: I am very new to this and would appreciate any help with this problem.
Example:
Source Tables:
Result Table:
This is how you combine all tables in workbook:
let
Source = Excel.CurrentWorkbook()
,Expand = Table.ExpandTableColumn(Source, "Content", {"Info T", "Info"}, {"Info T", "Info"})
in
Expand
Then you just do additional transformation that you need (probably pivot column).
Related
I am using cmd.transferspreadsheet in Excel VBA to import an Excel sheet into an Access Table. Every time I run this code, the data sent to access is 1 iteration out of date. I have zeroed in on two columns one that has a tier assignment (column 4) and one that has a string for the time and person who is submitting (column 13). In stepping through the code, I am printing the values of these 2 columns to the immediate window. I am doing that both by a range reference and an object reference. Both give me the correct answer, but when I go into Access, I see the data that was there before I changed it. What am I doing wrong?!?!?!
Sub SendTiersToDB()
'sends the data from this file to the access database
Dim fPathName As String
Dim dbTblTiers As String
Dim strSubmit As String
Dim tblXLTiers As ListObject
Dim strXLTiers As String
Dim appDB As New Access.Application
Set tblXLTiers = Sheet7.ListObjects(1)
fPathName = "\\MERCH\Assortment Planning\Databases\New_AP_Database.accdb"
strXLTiers = tblXLTiers.DataBodyRange.Address
dbTblTiers = "Tbl_Tiers"
'Fill In Subbmission Data
strSubmit = "Last Submitted: " & Now & " by " & Environ("username")
tblXLTiers.ListColumns(13).DataBodyRange.Value = strSubmit
tblXLTiers.ListColumns(13).DataBodyRange.Calculate
'and insert the new store records
Debug.Print "By Range " & Sheet7.Range("D2").Value
Debug.Print "By Range " & Sheet7.Range("M2").Value
Debug.Print "By Object " & tblXLTiers.DataBodyRange(1, 4)
Debug.Print "By Object " & tblXLTiers.DataBodyRange(1, 13)
appDB.OpenCurrentDatabase fPathName
appDB.DoCmd.TransferSpreadsheet _
TransferType:=acImport, _
SpreadSheetType:=acSpreadsheetTypeExcel12Xml, _
TableName:=dbTblTiers, _
Filename:="https://theexcelfilepath/file.xlsb", _
HasFieldNames:=True, _
Range:=Sheet7.Name & "$" & "A1:O9277"
End Sub
Thank you for the comments! I woke up at 4AM with the same thought.
I added a line to save the file prior to the DoCmd line, and that did the trick!
'Fill In Subbmission Data
strSubmit = "Last Submitted: " & Now & " by " & Environ("username")
tblXLTiers.ListColumns(13).DataBodyRange.Value = strSubmit
tblXLTiers.ListColumns(13).DataBodyRange.Calculate
ThisWorkbook.Save
'and insert the new store records
appDB.OpenCurrentDatabase fPathName
appDB.DoCmd.TransferSpreadsheet _
TransferType:=acImport, _
SpreadSheetType:=acSpreadsheetTypeExcel12Xml, _
TableName:=dbTblTiers, _
Filename:="https://theexcelfilepath/file.xlsb", _
HasFieldNames:=True, _
Range:=Sheet7.Name & "$" & "A1:O9277"
I have some vArrays which are not clearing out. The purspose of the macro is to work on a raw data tab which has 30+ tabs, each tab holding information for a specific office, 001-New York, etc. The macro is supposed to select x number of tabs (based on a reference file), copy them and save them into a new workbook. The problem is that instead of copying and saving from the raw data file it save the reference file instead. A For...Next loop is used to determine which tabs/offices to select & copy from the raw data file. The varrays are inside the loop and contain the names of the offices. When the code encounters the vArray the varray values are not clearing out when the loop circles back around.
Example:
'For 1' reference a cell with value of "8" so it populates 8 different vArray values (offices in this case). 'For 2' has a reference number of 5 and is supposed to populate 5 vArray values. It does this correctly as I can see the 5 new values in the locals window under vArray (1) thru vArray (5), however, vArray 6 thru 8 are showing values of the previous loop instead of 'Empty'. The vArray values are not clearing out when the macro loops.
sMasterListWBName is the reference file which tells the macro which tabs to copy from the raw data file and where to move the newly created workbook. The sub is also copying, saving, and distributing the reference file instead of the raw data file for some iterations of the loop (secondary issue--I will try to refrain from splitting the thread topic).
Thanks in advance to anyone who tries to answer this question.
Option Explicit
Dim iYear As Integer, iMonth As Integer, iVer As Integer, icount As Integer, iCount2 As Integer
Dim iLetter As String, iReport As String
Dim sMonth As String, sDate As String, sVer As String, sAnswer As String
Dim sFolderName As String, sManagerInitials As String
Dim iManagerNumber As Integer, iManagerStart As Integer, iTabNumber As Integer, iTabStart As Integer
Dim sMasterListWBName As String, sConsolidatedWBName As String, sExists As String
Dim oSheet As Object, oDistList As Object
Dim vArray(300) As Variant
Dim wbDistList As Workbook
Dim wsAgentListSheet As Worksheet, wsMain As Worksheet
Dim rCell As Range, rCell2 As Range, rCellTotal As Range
Public sFINorAgent As String
Sub Agent_Distribute()
On Error Resume Next
iYear = frm_fin_rep_main_distribute.txt_year
iMonth = frm_fin_rep_main_distribute.txt_month
iVer = frm_fin_rep_main_distribute.txt_version
sMonth = Right("0" & iMonth, 2)
sDate = iYear & "." & sMonth
sVer = "V" & iVer
sAnswer = MsgBox("Is the following information correct?" & vbNewLine & vbNewLine & _
"Report - " & frm_fin_rep_main.sLetter & vbNewLine & _
"Year - " & iYear & vbNewLine & _
"Month - " & sMonth & vbNewLine & _
"Name - " & frm_fin_rep_main.sReport & vbNewLine & _
"Version - " & sVer, vbYesNo + vbInformation, "Please verify...")
If sAnswer <> vbYes Then
Exit Sub
End If
Unload frm_fin_rep_main_distribute
frm_agent.Hide
Form_Progress
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
sConsolidatedWBName = ActiveWorkbook.Name
sMasterListWBName = "Dist Master List Final.xls"
If Not IsFileOpen(sMasterListWBName) Then
Workbooks.Open FileName:= _
"W:\Addins\01 GL - Distribution\" & sMasterListWBName, Password:="password"
Workbooks(sConsolidatedWBName).Activate
End If
Set oDistList = Workbooks(sMasterListWBName).Worksheets("Agent")
With oDistList
iManagerNumber = .Range("ManNumber2") 'range value = 66
For iManagerStart = 2 To iManagerNumber '2 to 66
If .Range("A" & iManagerStart) = "x" Then
iTabNumber = .Range("E" & iManagerStart) 'E2 to E66
sFolderName = .Range("F" & iManagerStart) 'F2 to F66
sManagerInitials = .Range("G" & iManagerStart) 'G2 to G66
For iTabStart = 1 To iTabNumber
vArray(iTabStart) = .Range("G" & iManagerStart).Offset(0, iTabStart)
Next iTabStart
If iTabNumber = 1 Then
Sheets(vArray(1)).Select
Else
Sheets(vArray(1)).Select
For iTabStart = 2 To iTabNumber
Sheets(vArray(iTabStart)).Select False
Next iTabStart
End If
ActiveWindow.SelectedSheets.Copy
' *** the following code is optional, remove preceding apostrophes from the following four lines to enable password protection ***
'For Each oSheet In ActiveWorkbook.Sheets
'oSheet.Protect "password"
'oSheet.EnableSelection = xlNoSelection
'Next
ActiveWorkbook.SaveAs FileName:= _
"W:\Financials\" & iYear & "\" & sDate & "\Report to Distribute Electronically\Department Reports\" _
& sFolderName & "\Current Year Financials" & "\" & "Y" & ") " & iYear & "-" & sMonth & " Agent Report Card " & sVer & " - " & sManagerInitials & ".xls"
ActiveWorkbook.Close
End If
iPercent = iManagerStart / iManagerNumber * 95
Task_Progress (iPercent)
Next iManagerStart
End With
Workbooks(sMasterListWBName).Close False
Task_Progress (100)
Unload frm_progress
Set oDistList = Nothing
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Message_Done
frm_agent.Show (vbModeless)
End Sub
I fixed it. I just added "Workbooks(sWbName).activate" at the end of the loop to make sure the focus is back on the raw data file. Now all files are saving in the correct format and location. Case closed unless someone has anything else to add. Maybe someone knows the reason the macro was losing sight of its active sheet (saving reference file instead of raw data file). Thank you.
Good Afternoon,
I have created a Macro that uploads data to a access database ( both on my desktop). The problem is it I keep getting errors when I try to expand the range.
I presumed it would be something simple but seems to be something I am overlooking.
here is the code - basically I would like to include the column or set it to a dynamic range? can you please help?
Sub AccessCode()
Application.ScreenUpdating = False
Dim db As Database
Dim rs As DAO.Recordset
Set db = OpenDatabase("C:\Users\user\Desktop\Test Copy.accdb")
Set rs = db.OpenRecordset("Fact Table", dbOpenTable)
rs.AddNew
rs.Fields("GUID") = Range("g2").Value
rs.Fields("StageID") = Range("h2").Value
rs.Fields("Sync Date") = Range("i2").Value
rs.Fields("Forecast HP") = Range("j2").Value
rs.Fields("Owner Id") = Range("k2").Value
rs.Fields("Recent Modified Flag") = Range("L2").Value
rs.Fields("Upload Date") = Range("M2").Value
rs.Update
rs.Close
db.Close
Application.ScreenUpdating = True
MsgBox " Upload To PMO Database Successful."
End Sub
You can use a query instead of iterating through a recordset:
Sub AccessCode()
Application.ScreenUpdating = False
Dim db As Database
Dim rs As DAO.Recordset
Set db = OpenDatabase("C:\Users\user\Desktop\Test Copy.accdb")
db.Execute "INSERT INTO [Fact Table] ([GUID], [StageID], etc) " & _
"SELECT * FROM [SheetName$G:M] " & _
"IN """ & ActiveWorkbook.FullName & """'Excel 12.0 Macro;HDR=No;'"
End Sub
This has numerous advantages, such as often being faster because you don't have to iterate through all the fields.
If you would trigger the import from Access instead of Excel, you wouldn't even need VBA to execute the query.
Change the rs section to this one:
With rs
.addnew
!GUID = Range("g2").Value
!StageID = Range("h2").Value
'...etc
.Update
End With
MSDN source
Use the AddNew method to create and add a new record in the Recordset object named by recordset. This method sets the fields to default values, and if no default values are specified, it sets the fields to Null (the default values specified for a table-type Recordset).
After you modify the new record, use the Update method to save the changes and add the record to the Recordset. No changes occur in the database until you use the Update method.
Edit:
This is how your code should look like, when you change the rs section with the code above:
Sub AccessCode()
Application.ScreenUpdating = False
Dim db As Database
Dim rs As DAO.Recordset
Set db = OpenDatabase("C:\Users\user\Desktop\Test Copy.accdb")
Set rs = db.OpenRecordset("Fact Table", dbOpenTable)
With rs
.addnew
!GUID = Range("g2").Value
!StageID = Range("h2").Value
'...etc
.Update
.Close
End With
Application.ScreenUpdating = True
MsgBox " Upload To PMO Database Successful."
End Sub
Just thought I'd add in an alternative to #Erik von Asmuth's excellent answer. I use something like this in a real project. It's a little more robust for importing a dynamic range.
Public Sub ImportFromWorksheet(sht As Worksheet)
Dim strFile As String, strCon As String
strFile = sht.Parent.FullName
strCon = "Excel 12.0;HDR=Yes;Database=" & strFile
Dim strSql As String, sqlTransferFromExcel As String
Dim row As Long
row = sht.Range("A3").End(xlDown).row
Dim rng As Range
sqlTransferFromExcel = " Insert into YourTable( " & _
" [GUID] " & _
" ,StageID " & _
" ,[sync Date] " & _
" ,[etc...] " & _
" ) " & _
" SELECT [GUID] " & _
" ,StageID " & _
" ,[sync Date] " & _
" ,[etc...] " & _
" FROM [{{connString}}].[{{sheetName}}$G2:M{{lastRow}}]"
sqlTransferFromExcel = Replace(sqlTransferFromExcel, "{{lastRow}}", row)
sqlTransferFromExcel = Replace(sqlTransferFromExcel, "{{connString}}", strCon)
sqlTransferFromExcel = Replace(sqlTransferFromExcel, "{{sheetName}}", sht.Name)
CurrentDb.Execute sqlTransferFromExcel
End Sub
My code I've written so far takes the username, computername and current datetime and writes it to a SQL table. What I want to do is capture when different sheets are selected and the timestamp associated with each.
Sub UpdateTable()
Dim cnn As ADODB.Connection
Dim uSQL As String
Dim strText As String
Dim strDate As Date
Dim strUsername As String
Dim strComputerName As Variant
strUsername = Environ("username")
strComputerName = Environ("Computername")
strDate = Now
Set cnn = New Connection
cnnstr = "Provider=SQLOLEDB; " & _
"Data Source=icl-analive; " & _
"Initial Catalog=DW_ALL;" & _
"User ID=dw_all_readonlyuser;" & _
"Trusted_Connection=Yes;"
cnn.Open cnnstr
uSQL = "INSERT INTO Audit (UN,CN,DT) VALUES ('" & strUsername & "','" & strComputerName & "','" & strDate & "');" ''
Debug.Print uSQL
cnn.Execute uSQL
cnn.Close
Set cnn = Nothing
Exit Sub
End Sub
I'm not too sure if I'm oversimplifying this but from what I understand:
Add the UpdateTable() sub to a Module. Add a Worksheet_Activate() event to each worksheet you want to run UpdateTable().
When the worksheet is selected the UpdateTable() sub will run, which will add the username, computer name and timestamp to your database. It may be worth adding the worksheet name (if you're looking to run this across multiple worksheets)
Therefore, when a worksheet is selected you could add the Application.ActiveSheet.Name and any other values (timestamp, user, etc) to the database.
I'm trying to copy a worksheet ("ReceivingRecords") from the workbook ("InventoryControlSystemV1.1") and paste it in a new workbook ("RecordBook"). I have created a temporary workbook named "Temp.xls" which allows me to use the SaveCopyAs method to create my new workbook "RecordBook".
When I run the procedure, "RecordBook" is created as intended but with only one entry (The text 'InventoryControlSystemV1.1.xls') in cell A1.
The worksheet that I want to copy is then pasted into a new, unnamed, workbook.
I can't figure out where or why this new workbook is being created.
Here is the code for this procedure:
Sub WriteReceivingToRecords()
Dim UsedRng As Range
Dim LastCol As Long
Dim BeginDate, EndDate
Dim NameString
Dim FormatBeginDate, FormatEndDate
Dim BackupQuest As Integer
Dim BackupMsg As String
'Confirmation dialog box to avoid mistakes
BackupMsg = "This will create a new workbook for the period" & vbNewLine
BackupMsg = BackupMsg & " since the last backup was made, and will clear" & vbNewLine
BackupMsg = BackupMsg & " the receiving records in this workbook." & vbNewLine & vbNewLine
BackupMsg = BackupMsg & "Are you sure you want to back up the receiving records?"
BackupQuest = MsgBox(BackupMsg, vbYesNo, "Back-up Records")
If BackupQuest = vbNo Then
Exit Sub
Else
' Find start and end dates of receiving - To use for worksheet title
Workbooks("InventoryControlSystemV1.1.xls").Activate
Worksheets("ReceivingRecords").Activate
Set UsedRng = ActiveSheet.UsedRange
LastCol = UsedRng(UsedRng.Cells.Count).Column
Do While Cells(2, LastCol) = ""
LastCol = LastCol - 1
Loop
EndDate = Cells(2, LastCol).Text
BeginDate = Cells(2, 2).Text
FormatBeginDate = Format(BeginDate, "d mmmm yy")
FormatEndDate = Format(EndDate, "d mmmm yy")
NameString = "M-Props Receiving Records " & FormatBeginDate & " To " _
& FormatEndDate & ".xls"
Workbooks("InventoryControlSystemV1.1.xls").Sheets("ReceivingRecords").Copy
Workbooks.Open Filename:="Temp.xls"
Workbooks("Temp.xls").Activate
Workbooks("Temp.xls").Worksheets("Sheet1").Paste _
Destination:=Workbooks("Temp.xls").Worksheets("Sheet1").Range("A1")
Workbooks("Temp.xls").SaveCopyAs NameString & ".xls"
Workbooks("Temp.xls").Close False
End If
End Sub
Replace
Workbooks("InventoryControlSystemV1.1.xls").Sheets("ReceivingRecords").Copy
with
Workbooks("InventoryControlSystemV1.1.xls").Sheets("ReceivingRecords").Cells.Copy
That should do it.