I've got an Access 2007 database on which I have created around 15 SQL queries to process specific data, I have created a main frame navigation menu using menus in Access, I now need to extract all th queries to Excel using VBA code, I have managed to do this with the code below by creating a button and specifying this code to it.
Private Sub query1_Click()
DoCmd.TransferSpreadsheet acExport, _
acSpreadsheetTypeExcel9, "Total Users and Sessions", _
"C:\UsersandSessions.xls", , "Total Users & Sessions"
End Sub
Now my problem at the moment is that fine the query is exported to Excel, but it is done so without any formatting applied at all, I would like to add some formatting at least to the headers and maybe a title inside the spreadsheet, and one thing I dont really like is that all records are being started from the first cell. Also I would prefer that if I hit that button again in Access and the Excel spreadsheet has already exists with that query output then when clicked again it will write again to a the next available sheet.
Any suggestions or ideas a very welcome.
The short story, is you can't. You might be able to do some scripting on the Excel side to format the resulting file. If you want something pretty, you probably want to create a report.
You could, instead mount the excel sheet as a table, and then on a separated sheet in the excel file, reference the first sheet, and format the second sheet for viewing.
if you use DoCmd.TransferSpreadsheet and create an original and then edit it so that the formatting is correct, you can then run DoCmd.TransferSpreadsheet again and it will update the file with the values but keep the formatting.
However, if a human then changes the file by adding new tabs, or adding calculations, etc, then the DoCmd.TransferSpreadsheet will no longer work and will fail with an ugly error message. So what we do in our enviroment is DoCmd.TransferSpreadsheet to an original file with formatting, and follow that up in the VBA by copying the file to the users desktop, and then opening that copy so the user doesn't mess up the original source excel file.
This approach is a minimum code, clean, and easy to maintain solution. But it does require a extra "source" or original file to be hanging around. Works in Access 2007.
You also would like the results to end up on a new tab. Unfortunately, I think it will take some excel automation to do that. The VBA inside Acccess can call a function inside the VBA in Excel. That VBA could then copy the tabs as needed.
My idea would be a hybrid of Excel automation from Access and creating a template in Excel as well that would have a data table linked to your query.
To start create your data table in Excel. You can start three rows down and two columns to the right if you want or wherever. Go to your data tab and click access, find your db, choose your query you want to link to, choose table as the radio button but click properties next instead of ok, uncheck the enable background refresh, this part is critical ... under the definition tab in the connection string you will see a part that says Mode=Share Deny Write change that to Mode=Read, this will make sure that the query refreshes without errors from an MS Access VBA while the db is open and will keep your users from writing back to the db in case your query is a writeable query. Once you set that up you can adjust the table formatting however you choose from the table design tab and it will keep that formatting.
For the purposes of this we are going to assume you started the table in cell B4 ,and your named the worksheet CurrentDay, for purpose of the following VBA example be sure to replace that reference with your actual placement.
Next go back to Access and write your VBA first ensure that in your VBA window you have the reference to Microsoft Excel 12.0 Object Library is selected by going to Tools > References and selecting it from the alphabetical listing.
Create your sub as follows:
Sub query1_click()
Dim xl as Excel.Application
Dim wbk as Excel.Workbook
Dim wks as Excel.Worksheet
Dim RC as Integer
Dim CC as Integer
Set xl = New Excel.Application
Set wbk = xl.wbk.Open "X:\Filelocation\FileName.xlsx" 'name and path you saved the file you previously created
xl.Visible = True
'The above is not necessary but you may want to see your process work the first few times and it will be easier than going to task manager to end Excel if something fails.
RC = xl.Application.CountA(xl.wbk.Worksheets("CurrentDay").Range("B:B")) + 3 'This will count the rows of data in your table including your header so you can copy the data to another tab dynamically as the size of your table expands and shrinks we add 3 to it because we started at row 4 and we need the location of the last row of the record set.
CC = xl.Application.CountA(xl.wbk.Worksheets("CurrentDay").Range("4:4")) + 1 'This counts the header row and adds one space because we will use this as a location holder for our copy / paste function
Set wks = xl.wbk.Worksheets.Add
wks.Name = format(date(),"MM_dd_yy") 'this will name the tab with today's date... you can eliminate this step if you just want the sheets to be the generic Sheet1, Sheet2, etc.
With xl.wbk
.Worksheets("CurrentDay").Range(Cells(4,2),Cells(RC,CC)).Copy
.wks.PasteSpecial xlPasteValues 'This pastes the values so that the table links do not paste otherwise every tab would just refresh everyday.
.wks.PasteSpecial xlPasteFormats 'This gets your formatting.
.RefreshAll 'This will refresh your table
Wend
With xl
.Save
.Close False
.Quit
Wend
Set xl = Nothing
Set wbk = Nothing
Set wks = Nothing
End Sub
That should get you to have your data to not start on A1 of your sheets, save your old data each time, and automate the steps from access.
Related
I can perhaps better describe what I want by way of example. I have two Excel workbooks. I want to essentially copy one over to a new workbook as it is (basic copy/paste of everything), and then copy over a specific portion of the other workbook and paste it at the end of the new workbook.
So, there are two input workbooks and one output workbook. And that is how I would like the macro to be structured: There should be a "master" workbook that contains the macro (button) where the two input workbooks are specified by their folder path, and there is an output folder for the new combined workbook (see the very end for example of how I'd like it specified). The first of the input workbooks should have everything in it copied over. Note that this should be dynamic, and should not dictate a specific cell range (as cell ranges could vary). An example of the first input workbook is shown below:
Everything is to be copied over. I don't think that's much of an issue. The tricky part (for me) is to then take a specific portion of the second input workbook and copy/paste that to the end of the first input workbook. I am finding this difficult because the second input folder should be selected by a specific header and then everything under that header should be copied (i.e. select header and then CTRL+SHIFT+DOWN). For example:
This should take "Maturity[5,)" (highlighted in yellow) as the header and then select everything under that, copy it, and then paste it to the matching header at the end of the first input workbook (second row, i.e., Row 8, which, by the way, is the format for all workbooks for this: Rows 1:6 are empty, but should stay like that).
Hopefully you're still with me. After this is done, the new (combined) workbook should look something like this:
Just even getting this far would be extremely helpful. But there is one more issue. By copying over the entirety of the first input workbook, there is an additional header (Row 7; "L-OAS" in the above picture). This header needs to be merged and centered across the range of the new combined workbook, including the second input workbook portion added:
And that's it. Once that header has been merged and centered in the new workbook, the macro should save the new workbook in the output path specified.
Example of input/output paths to be specified:
EDIT: The below is my attempt at doing part (1) -- that is, just copying and pasting the first workbook.
Sub Combine()
Dim wb1 As Workbook
Dim wb2 As Workbook
Dim wsSource As Worksheet, ws As Worksheet
Application.ScreenUpdating = False
shtOutput1.Visible = xlSheetVisible
shtOutput1.Cells.Clear
Set outputRange = shtOutput1.Cells(1, 1)
Set wb1 = Workbooks.Open(Range("USDInputFile1").Value, False, True)
For Each wsSource In wb1.Worksheets
If InStr(wsSource.Cells(1, 1).Value, "Bloomberg") > 0 Then
With wsSource
wsSource.Copy shtOutput1
End With
End If
Next
wb1.Close False
Application.ScreenUpdating = True
With Workbooks.Add
shtOutput1.Copy .Worksheets(1)
Application.DisplayAlerts = False
For Each ws In .Worksheets
Next ws
Application.DisplayAlerts = True
.Close True, ThisWorkbook.Names("OutputFolder").RefersToRange.Value + Application.PathSeparator + "USD Combined Output.xlsx"
End With
shtOutput1.Cells.Clear
shtOutput1.Visible = xlSheetHidden
End Sub
"shtOutput1" is a worksheet in the master workbook (where the macro
button and file paths are located
"USDInputFile1" is a named Cell in the master workbook that leads to
the file path for the first input workbook (i.e. "wb1" -- "wb2" will
be the second input workbook)
Lets just take a step back and look at this from a scalability stance.
You don't want a button to combine things because there is a lot to go wrong and a fair bit of babysitting you will have to do VBA wise and on top of that pressing is a button... ppffft that's work...
No what you want is a seamless update where anything added to Maturity 5 or Maturity 10 will automatically update to your output leaving them to just grow on their own untouched downward like Microsoft intended.
First lets look at the structure, there is some, they have headers but they are not tables and they both have a common ID Date, So change your bottom most headers to ensure they make sense and are Uniform, Unique and Consistant across all iterations of each table. So Mat5_Total or Mat10_Total etc for however many you have.
Insert Tab - Table - Give them proper Shorthand Table names like tblMat10 and tblMat5
Lets open a New File and get them in with Data Tab in the ribbon - External Data - Tranform - Little Down arrow on Close an Load and you only want this in the datamodel not loaded to the Spreadsheet So check Create Connection Only and Add this data to the Datamodel. Uncheck everything else there is no need to clutter your output.
Repeat 3 but for Mat10 only when you get to Close an load instead look to the Right and hit the down arrow on Merge Queries - as a new Query .
Now a new window will pop up Select the two queries and Join them on their Dates as the ID
Now you can close and load to the a sheet.
Give it a spin add some new data to Mat5 or Mat10 and refresh your newly created linking Sheet.
This works for all kinds of data, you can trial this with stocks, shares, CSV, ODBC Connections all sorts of data connections. all you need is a properly formatted infrastructure aka properly formatted tables!
A bonus to this method is if the tables evolve, modifying your Power Query is easy. Just rememeber to go into your connection settings and select refresh on open and if you like set it to refresh every 5 minutes.
7 Optional Bonus - Once this output table has been made it will be compatible with any external programs, because it is a correctly formatted output table.
UPDATING QUESTION to include reference I found here***:
Macro button under customized ribbon tab tries to open old Excel file
I wrote a thing here last night that this is related to (Excel telling me it can't access a file I don't want it to access) I've got a Sub that turns a portion of a worksheet into a table and then sorts that table by a column, in preparation for a second procedure that will copy areas of this sheet onto another sheet.
I had been getting an error that said "we can't connect to 'https://...my.sharepoint../BETAV9_8_ItemAccountingTEMPLATEetcetc. Please make sure you're using correct web address."
Now the code is opening a completely different version of this file, that is, BETAV9_9, which I guess it found, unlike before, when it couldn't find BETAV9_8. The thing is, I have no idea what's making it open the other file. Here is what I'm doing:
Sub makeItemChecklistTable()
Dim selectRange As String
Dim lowerBound As Integer
Dim ws As Worksheet
Dim src As Range
Dim tbl As ListObject
Dim sortRange As Range
lowerBound = numberofCERTSRecords() + 1 'This function counts the number of records on another sheet, which is 147
selectRange = "B1:I" & CStr(lowerBound)
Set ws = ActiveWorkbook.Sheets("ItemChecklist")
Set src = ws.Range(selectRange)
ws.ListObjects.Add(SourceType:=xlSrcRange, Source:=src, _
xlListObjectHasHeaders:=xlYes, tablestyleName:="TableStyleLight1").Name = "keywordChecklist"
'Item Checklist table is called keywordChecklist. Before exporting to PTF, sort keywordChecklist by Status in Ascending order
Set tbl = ws.ListObjects("keywordChecklist")
Set sortRange = ws.Range("keywordChecklist[ED_Question_ID]")
With tbl.Sort
.SortFields.Clear
.SortFields.Add Key:=sortRange, Order:=xlAscending
.Apply
End With
End Sub
I mentioned in last night's post that I used MS' guide for finding references to an external Excel file (https://support.microsoft.com/en-us/office/find-links-external-references-in-a-workbook-fcbf4576-3aab-4029-ba25-54313a532ff1?ui=en-us&rs=en-us&ad=us) but at least in all the places I looked, I can't find anything. And if there were a reference to an old file, why would the version before this one try to open BETAv9_8 and now the current workbook is successfully opening BETAv9_9?
When I stepped through this procedure, it inexplicably jumped (2000 times) to another procedure used to modify 2000 strings on a separate worksheet (it takes a string and returns the same string minus a few characters). That worksheet is referred to by the current worksheet via a VLOOKUP function but by the time this procedure has run, all those VLOOKUPs have already been looked up on the current page, i.e. they are displayed already in column H. I'm sorting by column E (ED_Question_ID).
***UPDATE
I do see that I have buttons assigned to macros on my custom toolbar that refer to older versions of this same file. I can fix this using the Export UI function described on this page (Macro button under customized ribbon tab tries to open old Excel file) but 1. I wasn't calling this macro from a button; I was running the procedure directly from the vba code window, and 2. Like I said I've been saving these files in sequence, raising the beta number every time I make a major change. Am I going to have to export and rename all the buttons/toolbar shortcuts every single time I save as a new file name? This seems like an absurd way to set things up by Microsoft.
I am looking at inserting/pasting a range of text data (40 columns) from Excel into bookmarks in Word. Most of the answers are done using Excel VBA, which is so not practical for my use case as I will have the Word document open, add a button that would run this 'import data' macro. I actually already have a button in the doc that inserts images into bookmarks, so that's one more reason I don't want to do it via Excel VBA.
I know this is not great code, but for the lack of definite leads, I'm throwing it here and hope that this gives you an idea of what I'm trying to achieve:
Sub ImportData()
Workbooks.Open ("\Book2.xlsm")
ActiveWindow.WindowState = xlMinimized
ThisWorkbook.Activate
Windows("Book2.xlsm").Activate
Range("A1:AF1").Select
Selection.Copy
Documents("test.docm").Activate
Selection.GoTo What:=wdGoToBookmark, Name:="Overlay_1"
Selection.Paste
End Sub
PS: It would be great if I could sort of 'transpose' the 40 columns into rows as it is pasted in Word.
Here's an update to my code based off #Variatus 's advice:
Sub ImportData()
Dim wb As Workbooks
Dim ws As Worksheets
Dim objSheet As Object
Dim objWord As Object
Set objWord = CreateObject("Word.Application")
wb.Open ("C:\Users\pc\Documents\Book2.xlsm")
Set objSheet = CreateObject("Excel.Application")
ActiveWindow.WindowState = xlMinimized
Set ws = Workbooks("Book2.xlsm").Sheets("Sheet1")
ws.Range("A1").Value.Copy
With objWord.ActiveDocument
.Bookmarks("Bookmark_1").Range.Text = ws.Range("A1").Value
End With
End Sub
I'm getting this error:
Runtime Error '91':
Object variable or With block variable not set.
Notice how I stuck with a single cell reference for now (A1). I'll just update my code as I learn along the way :)
When you click the button in your Word document you want the following sequence to be initiated.
Create an Excel application object. Make sure that a reference to Excel has been set (VBE > Tools > References) so that Excel's VBA objects are available.
Using the Excel application object, open the workbook. Create an object. Place the object in an invisible window.
Definitely forget about activating or selecting anything in either the workbook or your Word document. The latter is active and remains active from beginning to end. The bookmarks are points in your document you can reference and manipulate by name without selecting them. The Excel workbook is invisible. You can access any part of it using the Range object.
The data you want from your workbook are contained in Worksheets. Be sure to create an object for the worksheet you are about to draw data from.
Excel tables don't translate very well into Word tables. If you do want to go that way I suggest that you use VBA to create the table you want in Excel (transpose the data before you import them into Word). However, you may find it easier to first create the tables you want in Word and then just copy values from your Excel source into the word tables. That would involve taking one cell value at a time and placing it into one Word table cell. Transposing would be done by the algorithm you employ.
Close the workbook. Quit the Excel application. Set the Excel application = Nothing. At the end of your macro everything is as it was before except that your document has data in it which it didn't have before.
Each of the above six points will lead you to at least one question which you can ask here after you have googled the subject and written some code. In fact, I strongly urge you to create one Main procedure (the one which responds to your button click) and let that procedure call various subs which carry out the individual tasks and functions to support the subs. The smaller the parts you create the easier it is to write the code, to find questions to ask and get answers to them. If you plan your project well expect to have about 12 procedures in it by the time you are done. Good luck!
I have a pre-defined Excel template which i would be filling it with data using .Net C# code.
I know the size of some columns but some of them i don't know, so when i export data into the excel, the presentation doesn't look good.
This xls sheet is sent to customer. 1000's of documents generated every day like this. It's a tiresome work to every time open a newly generated excel document and change the width of the column to autofit by double clicking the column.
Is it possible in Microsoft Excel to AutoFit the size some column and not for few other columns?
range("a2:d2").Columns.AutoFit
You could also use something like
range("a2").CurrentRegion.Columns.AutoFit
or
range("a:d, f:g").Columns.AutoFit
If you want the VBA for it is:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Application.ScreenUpdating = False
For Each Value In Target.Columns
Worksheets(Sh.Name).Columns(Value.Column).AutoFit
Next Value
Application.ScreenUpdating = True
End Sub
Go to the Developer Tab (have to turn it on via File>Options>Customise Ribbon.
Open Visual Basic
Select Thisworkbook
Copy the Above VBA Code
Paste into the VBA Window
Close the VBA Window and the VBA editor
I am trying to look up some data in an excel sheet to populate a field in a Lotus Notes app on demand. I am using an Action button with LotusScript like so:
Dim v As String
Dim v2 As String
'Open XL sheet for access to data
Set oExcel = CreateObject ( "Excel.Application" )
v="\\msp2\mi\CSD\Confidential\IT and PPST budget.xlsm"
Msgbox("opening " & v)
Set oWorkbook = oExcel.workbooks.open(v)
Set oWorkSheet= oWorkbook.worksheets (4)
v2=Cstr(oWorkSheet.Cells(1,1).value)
Messagebox(v2)
This code does work in that it pulls data from cell A1 - but which sheet?
The sheet containing the data I want is "Sheet4" renamed as "Logic-Detail" but if I use 4 as a parameter as above I get data from the 4th sheet from the left. I need to be able to cope with sheets being hidden as well. I spent 20 minutes on MSDN's excel object model "help" getting nowhere :-(
I feel sure it must be dead easy when you know the answer !
Guessing at the correct syntax is frustrating, isn't it? When referring to sheets in Excel-VBA there are several options:
You can list them by index-number, as you already did in the code-sample in your question
You can refer to it by its name, in your case this would probably be oWorkbook.Worksheets("Logic-Detail")
You can refer to it by its codename, in your case this would probably be oWorkbook.Sheet4. The codename can be changed when you view the properties of the worksheet in the VBA editor.
There may be even more ways to refer to the sheet, but these are the ones which come to mind at the moment. As we know from the question and comments, at least the two first options also work in LotusScript.