I've been charged with refactoring and extending the functionality of an big Access database. The existing VBA code is pretty bad, so I'd like to clean it up by removing uneccessary functions and simplifying the rest. I wonder how I can find all usages of the functions?
As for the VBA project itself, I can of course search for their names. As for the SQL queries: I've written a function which prints all queries to the VBE's intermediate window, so I can search them as well.
But many functions are also used in forms and reports. Is there a way to find them all? There are a lot of complex forms/reports, so simply having a look at one control after the another or removing the function and testing if everything still works is not feasible at all.
Unfortunately, there's no search function (that I know of) in Access which includes form and report properties.
However, there's an undocumented method SaveAsText that copies forms and reports into text files. If you extract all your forms and reports...
Dim db As Database
Dim d As Document
Set db = CurrentDb()
For Each d In db.Containers("Forms").Documents
Application.SaveAsText acForm, d.Name, "C:\export\" & d.Name & ".frm"
Next
For Each d In db.Containers("Reports").Documents
Application.SaveAsText acReport, d.Name, "C:\export\" & d.Name & ".rpt"
Next
...you can use "regular" text search tools (such as findstr, which is included in Windows) to find occurrences of certain words:
C:\export>findstr /I "\<myFunction\>" *
Form1.frm: OnClick ="=myFunction()"
Related
In MS Access it's possible to convert a Form to a Report using the "Save Object As" functionality (Office Button -> Save As -> Save Object As "Save the current database object as a new object").
I'd like to have the same functionality using VBA but it seems to me that none of the following relevant DoCmd methods are suitable CopyObject, OutputTo, Save, TransferDatabase.
I was unable to find any useful information, except a blog post in which a code sample is provided using the SendKeys statement, but it seems not working.
Any advice?
It is not possible to convert a form to a report using VBA. Even using Sendkeys (which really should be avoided as there is a potential for the wrong application to get the keypresses instead) is not an effective method, as some controls don't get converted correctly.
Instead, you should be designing a report from scratch, with the required headers/sorting/grouping - it will look far better.
Regards,
Thanks to some tips from MajP on an access-programmers forum post, I found a way that is suitable for my needs, although it uses the sendkey function:
strFormName = "formname"
strReportName = "reportname"
DoCmd.SelectObject acForm, strFormName, True
SendKeys strReportName & "~"
RunCommand acCmdSaveAsReport
strFormName must contain the name of the Form to be saved as a Report whose name must be contained in strReportName.
I spent alot of time to create 24 different separate MS Access queries that I have saved in the MS Access database. They have to be run in a specific order.
I want to run a macro that will just execute them one after another.
I know I can do this using VBA but I have copy all the SQL into VBA one at a time.
is there a way I can just have VBA run the saved queries in the MS Access database without copying them into VBA?
I am very good with SQL. 15+ years doing Oracle work, Code writing, DB Tuning, Data DBA work.
I am new to using SQL and VBA to run against MS Access DB.
To clear a few terminologies:
MS Access GUI comprises of five main object types: tables, queries, forms, macros, and modules. Technically, there is no such thing as VBA macros in MS Access. This is a terminology from MS Excel where each subroutine is defined as a macro.
In MS Access there is a physical object known as a macro which is divorced from VBA (though can call VBA code). Coded routines which includes all functions and subroutines in MS Access are encapsulated in the module (object) usually behind forms, reports, or standalone modules.
Do remember each Microsoft Office application maintains its own object library and should not be conflated with one another. VBA itself is a separate component installed by default (see Tools \ References in IDE). Any language (Java, PHP, Python, etc.) that can run COM-interfacing can connect and run each Office app's object library. VBA is simply one type that does this.
MS Access maintains stored queries (as another object) which can be actions queries (UPDATE, INSERT even CREATE TABLE or ALTER TABLE) or recordset queries (SELECT). You do not have to copy all of SQL code inside VBA to run these saved query objects. In fact, stored queries run more efficiently than string queries called in code since the MS Access engine compiles the best execution plan with stored queries.
Having said that, consider a macro calling multiple OpenQuery actions or coded subroutine inside a module calling multiple DoCmd.OpenQuery commands
Macro
OpenQuery
QueryName: myfirstActionQuery
View: Datasheet
Data Mode: Edit
OpenQuery
QueryName: mySecondActionQuery
View: Datasheet
Data Mode: Edit
OpenQuery
QueryName: myThirdActionQuery
View: Datasheet
Data Mode: Edit
...
For action queries you do not have to close the query as OpenQuery runs process without physically opening anything to screen. You may want to incorporate the SetWarnings to False action in order to avoid the message prompt of how much records will be updated or inserted which is a default setting in Access. Because this is a potentially hazardous action, click Show All Actions to see this specific action.
Module
Public Sub RunQueries()
DoCmd.SetWarnings False
DoCmd.OpenQuery "myfirstSavedQuery"
DoCmd.OpenQuery "mySecondSavedQuery"
DoCmd.OpenQuery "myThirdSavedQuery"
...
DoCmd.SetWarnings True
End Sub
Similar to macros, you do not have to close action queries and can call the DoCmd.SetWarnings to suppress the message prompts with each update, insert, or make-table action.
The alternative to turning SetWarnings off and on is #LeeMac's solution which runs queries using the DAO's Database.Execute() command. In fact, because DAO is part of the MS Access object library, the above method can be run outside VBA as mentioned above.
Python
import os
import win32com.client
access_file = r'C:\Path\To\MyDatabase.accdb'
try:
oApp = win32com.client.Dispatch("Access.Application")
oApp.OpenCurrentDatabase(access_file)
db = oApp.Currentdb()
db.Execute("myFirstSavedQuery")
db.Execute("mySecondSavedQuery")
db.Execute("myThirdSavedQuery")
...
except Exception as e:
print(e)
finally:
oApp.Quit()
db = None; oApp = None
del db; del oApp
In its very simplest form, you can define a Sub such as:
Sub ExecuteQueries()
With CurrentDb
.Execute "MyQuery1"
.Execute "MyQuery2"
'...
.Execute "MyQueryN"
End With
End Sub
Or define this as a Function in a public module if you intend to invoke this from an MS Access Macro.
Here is another method if you don't have specific naming conventions utilizing an array for the query names in execution order:
Public Sub RunMasterUpdate()
Dim qryList As Variant
Dim i As Long
qryList = Array("QueryName1", "QueryName2", "QueryName3")
For i = LBound(qryList) To UBound(qryList)
CurrentDb.Execute qryList(i)
Next
End Sub
You can do something like this to run saved queries:
Dim db As DAO.Database, qry As DAO.QueryDef
Set db = CurrentDb
Debug.Print "-- Start --"
For Each qry In db.QueryDefs
If qry.Name Like pattern Then
Debug.Print qry.Name,
qry.Execute
Debug.Print "(" & qry.RecordsAffected & ")"
End If
Next
Debug.Print "-- End --"
db.Close()
Name the queries so that the alphabetical order matches the the expected execution order, e.g., 01_deleteCustomers, 02_appendCustomers, 03_etc.
Specify a pattern string or remove the If alltogether if you want to run all the queries.
Note that in the Visual Basic editor under menu Tools > References... I have selected Microsoft DAO 3.6 Object Library. (You might have another version)
I have a excel file with macro-enabled that is used to upload the data from excel into a database. There's a button in excel to run this code. When I run the code, it has a window pop-up "Select Data Source". Does anyone how to prevent this from popping up.
Thank You
Sadly as a beginner I cannot provide the picture so I will explain.
When the button is clicked. A window is opened named "Select Data Source" with three options dBASE Files, Excel Files, MS Access Database. I need to select one of them and continue on. For me it's excel so I click this and press okay. Then it has the option of allowing me to select the workbook. Is there a way to pre-do this before so that I don't see this window.
The code is shown Below, which gives this problem.
Sub Collect_campaigns()
Dim qryCampaigns As String
Application.ScreenUpdating = False
qryCampaigns = "SELECT * FROM test.tbl_test;"
Worksheets("Campaigns").Visible = True
Sheets("Campaigns").Select
Range("A:F").Select
Selection.Clear
Range("A1").Select
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
"ODBC;DSN=TEST_DATASOURCE;", Destination:=Range("$A$1")).QueryTable
.CommandText = qryCampaigns
.Refresh BackgroundQuery:=False
End With
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
"ODBC;DSN=TEST_DATASOURCE;", Destination:=Range("$A$1")).QueryTable
As you can see in the code above, your macro refers to ODBC DataSource named TEST_DATASOURCE. I can only assume that you have copied this file from other computer, if so you also need to create same ODBC Datasource on your machine too.
If your machine is running Windows you should check "Control Panel-->Administrative Tools-->Data Sources(ODBC)".
The pop-up window is a polite way of telling you that it cannot reconcile your datasource "ODBC;DSN=TEST_DATASOURCE;" with the available ODBC data sources. Your minimialistic approach is a good one, I think -- not putting the entire connection string in but just the name. It allows MS Query to fill in the blanks based on your ODBC connection settings).
Although less likely, it could also have something to do with the password, where the ODBC connection is rejecting the attempted use at the embedded password. Since you said your data source is Excel, I'm going to put that into the 'unlikely' category... unless your data source isn't really Excel and you were pulling our proverbial legs.
My recommendation would be to apply good programming practices: cheat and be lazy
Record macro
Create the table as you would normally in Excel, Data->From Other Sources->MS Query (or whatever)
Stop the macro
Now run the macro code on a clean workbook -- with any luck it will run fine. Then, strip out pieces of fluff code as you see fit and continue to re-test it until it breaks.
MS Access randomly deletes a SQL query's content when a non-vba macro exports the query contents to excel. The initial export works and the data is exported to Excel correctly, but then (about 50% of the time...) the SQL underlying the query goes missing. This is definitely triggered by the export (one can do a before and after comparison of the query).
The following site references the problem and talks about a VBA solution that automaticaly rebuilds the SQL query. I'd much prefer to just prevent this from happening rather than fixing it post-mortem. This forum post on bytes.com also discusses the issue. The suggested solution is not relevant.
The problem database was originally designed in Access 2007 and is now being used in Access 2010. All the queries and macros that are involved in this issue were created using Access 2007. These components were stable in Access 2007. This leads me to believe that the problem is not contained within the SQL. All the SQL queries are simple SELECT statements, there are no inserts, drop or make table commands.
I've just encountered this bug myself. None of the solutions posted here worked/were applicable to my case, and I wasn't 100% happy with the work-arounds offered, particularly the geeksengine.com one, which requires that you delete the Query entirely and re-create it from code that you've stored in VBA - not a very generalisable solution, and if someone else has to maintain your database, they'll find themselves perplexed to discover that the Query they changed occasionally reverts itself to its old code, seemingly at random.
So I took a different approach, and wrote a generic routine that stores the SQL code of a Query before exporting the Spreadsheet, and then restores the code immediately afterwards if it's been erased:
Sub SafeSendQuery(QueryName As String, FileType As Variant, EmailAddresses As String, SubjectLine As String, BodyText As String)
Dim QueryCode As String
'Access has a bug where it sometimes erases the SQL code of the Query it's sending as a spreadsheet
'This stores the code in a string, so that we can restore it if it's been erased
QueryCode = CurrentDb.QueryDefs(QueryName).SQL
DoCmd.SendObject ObjectType:=acSendQuery, ObjectName:=QueryName, OutputFormat:=FileType, To:=EmailAddresses, Subject:=SubjectLine, MessageText:=BodyText, EditMessage:=True
'If the SQL code was erased, restore it
If CurrentDb.QueryDefs(QueryName).SQL <> QueryCode Then
Debug.Print "SQL Code Missing, restoring it now."
CurrentDb.QueryDefs(QueryName).SQL = QueryCode
End If
End Sub
Note that I wrote this for DoCmd.SendObject, as my users need to email the spreadsheet, but it should work just as well for DoCmd.OutputTo - just replace the DoCmd.SendObject line with a DoCmd.OutputTo one, and modify the parameters as necessary.
This is what a subroutine that calls it looks like:
Sub EmailAQuery()
Dim QueryName As String
Dim FileType As Variant
Dim EmailAddresses As String
Dim SubjectLine As String
Dim BodyText As String
QueryName = "Quarterly Figures"
FileType = acFormatXLSX
EmailAddresses = "bob#company.com; Sally#company.com"
SubjectLine = "Quarterly Figures for " & Format(Date, "mmmm yyyy")
BodyText = "Please see the Quarterly figures data for " & Format(Date, "mmmm yyyy") & ", attached."
Call SafeSendQuery(QueryName, FileType, EmailAddresses, SubjectLine, BodyText)
End Sub
Did you try using the expression builder to represent the query in access language?
Double click 'criteria' in the query design window, and build the expression.
For example:
SELECT table1.field1
FROM table1 INNER JOIN table2 on table1.field1 LIKE table2.field1
This query would become cleared, even though it worked. Use the expression builder to build the "LIKE" expression. It should look like this:
SELECT table1.field1
FROM table1 INNER JOIN table2 on table1.field1 = table2.field1
WHERE ((([table1]![field1] LIKE [table2]![field1])));
I resolved the issue by modifying the query. Initially the query was joining three tables through both inner and outer joins. I simplified the query into just one join by using an intermediate table, after which Access ceases to delete the query after it runs the macro. This is definitely a bug, one where Access chokes when its macro is tasked to run a query with complex joins.
I experienced the same issue in Access 2010. I was able to resolve it by using an OpenQuery action before the ExportWithFormatting action and a CloseWindow – Query action after the ExportWithFormatting action in my macro. In this way, the query would open, then export, and then close all within the same macro. I also used a StopMacro action at the very end.
I had this problem in Access 2010.
For me this was caused by a bug that only occurs if I collected these queries in a group.
Once I removed them from the custom group and left them as unassigned objects, the queries remained intact.
this one is the screenshot on the visual basic report. as you can see it starts at august then december then february but it i already group by it on the access which is this.
this is a query in the access im not sure if i did the right thing but i is already ordered by the earliest .
here is the sql code from the ms access
SELECT MonthName(Month([Date_sold])) & ' ' & Year([Date_sold]) AS Month_Sold, Sum(tblSell.Total_Price) AS Total_Earnings, Sum(tblSell.Quantity_Bought) AS Total_Medicine_Sold, tblSell.Generic_name, tblSell.Brand_Name
FROM tblSell
GROUP BY MonthName(Month([Date_sold])) & ' ' & Year([Date_sold]), tblSell.Generic_name, tblSell.Brand_Name, Year([Date_sold]), Month([Date_sold])
ORDER BY Year([Date_sold]), Month([Date_sold]);
in short is it doesnt order by on my report on the visual basic it should show february first like the one on ms access but it shows august which is different.
Order by, I believe, gets over written by the reports Order By property in the Data tab, on the property sheet. Either set it there or make sure it is empty. Then set the reports properties like this:
Me.OrderBy = "[SomeField], [AnotherField]"
Me.OrderByOn = True
EDIT:
All Microsoft products allow developers access to a development environment using a language called VBA (Visual Basic for Applications). You can use this language to write programs and macros to enhance products. Take a look here, although it's a bit old it may still be relevant: http://visualbasic.about.com/od/learnvba/l/aa030803b.htm
For you, you will need to add the code I suggested to the OnLoad event of the report. To do this:
Open Access.
Open your report in Design View.
On the left hand side, you should see a tab called Property Sheet
Select the Event Tab
in the OnLoad event, click the "..." (ellipsis) button
This will bring up the VBA code.
Add This:
Private Sub Report_Load()
Me.OrderBy = "[SomeField], [AnotherField]"
Me.OrderByOn = True
End Sub
Try that and see if it works.
The reason why is it not sorting (I am having this same problem at work doing some shipment order reports) is because the code is not reading the date. It will list them aplhabetically because it is a text field and not a short date. I just went to the parameters and was able to fix it there.