I have many Subs in a form with the following code
Dim db As DAO.Database
Set db = CurrentDb()
Dim strSQL as String
db.Execute strSQL
Is it good practice to initialize db at the "form level" i.e. write first two lines of code above only once? If so, how should I do it?
This is somewhat different from MSAccess: Global vs Local Variable Declaration
I have this function in a Module:
Public Function currDB() As Database
Static currDB_v As Database
If currDB_v Is Nothing Then
Set currDB_v = CurrentDb
End If
Set currDB = currDB_v
End Function
And call CurrDB.Execute
If you have no error handling as you posted in Q, then you can also write function:
Public Sub ExecSQL(SQL as string)
CurrDB.Execute SQL
End Sub
Related
all I need to run a function that will run multiple reports in different access databases... ad DB to rule them all :)
here the structure: in my multi_report.accdb I have a form with a button that is running this code:
Private Sub Comando1_Click()
Dim db_report As String
Dim objAccess As Object
Dim ctr As Date
'TICKETS
db_report = "\\sdocenco01\OPC\11_SCL\TICKETS\ticket.accdb"
Set objAccess = GetObject(db_report) 'opens other db
objAccess.Visible = True
objAccess.Run "report_tickets" 'runs function in other db
objAccess.Quit 'quits other db
Set objAccess = Nothing
'PROCESSI
db_report = "\\sdocenco01\OPC\11_SCL\PROCESSI\utilita.accdb"
Set objAccess = GetObject(db_report)
objAccess.Visible = True
objAccess.Run "report_processi"
objAccess.Quit
Set objAccess = Nothing
...
in the line
objAccess.Run "report_tickets"
i get a run-time error '2517' routine report_tickets not found
I tried different codes like
objAccess.Run "ticket.report_tickets"
or
objAccess.Run "ticket.Database1.report_tickets"
but nothing seems to work
in the \sdocenco01\OPC\11_SCL\TICKETS\ticket.accdb i have this code:
Public Function report_tickets()
DoCmd.SetWarnings False
Dim cdb As DAO.Database, qdf As DAO.QueryDef
Set cdb = CurrentDb
...
End Function
First, according to the Docs, the project name must be included:
The name of the Function or Sub procedure to be run. If you are
calling a procedure in another database, use the project name and the
procedure name separated by a dot in the form:
"projectname.procedurename"
Next, I guess the "Report_" prefix confuses. Try renaming the functions and call them like:
objAccess.Run "YourProjectName.ReportTickets"
I want to replace Query1.Period parameter "May 2018" with "Jun 2018".
SQL:
SELECT DISTINCTROW Query1.Period, Query1.Portfolio_1
FROM Query1
WHERE (((Query1.Period)="May 2018"))
ORDER BY Query1.Portfolio_2, Query1.Department, Query1.Position_NO;
VBA code attempt:
Private Sub newChangePeriodCriteria_Click()
Dim qdf As DAO.QueryDef
Dim qdfOLD As String
Set qdf = CurrentDb.QueryDefs("Query1_Mth")
With qdf
qdf.SQL = sqlString
.SQL = Replace(.SQL, "Period='May 2018'", "Period='Jun 2018'")
' Code to do stuff with SQL-string/query
' .SQL = qdfOLD ' Reset SQL to old setting
qdfOLD = .SQL
' DoCmd.RunSQL (Replace("Period='May 2018'", "Period='Jun 2018'"))
End With
Set qdf = Nothing
End Sub
Your code sets the SQL string of the query to a string from a variable sqlString (where does it come from?), then replaces "Period='May 2018'" by "Period='Jun 2018'" in the SQL of the query, then saves the final SQL in a variable qdfOld (which isn't used and which is a string variable, so the prefix qdf is misleading). The string you are trying to find and replace is not contained in your query, because in the query, Query1.Period is enclosed in brackets.
Supposed that the variable sqlString contains the original query string, you most likely wanted to do something like this:
Private Sub newChangePeriodCriteria_Click()
With CurrentDb
With .QueryDefs("Query1_Mth")
.SQL = Replace(sqlString, "(Query1.Period)='May 2018'", "(Query1.Period)='Jun 2018'")
.Close
End With
End With
End Sub
This is quite unusual, please take a look at query parameters (and the link that #ahleedawg provided).
Added:
Instead of opening the query in datasheet view, you can open a form in datasheet view. This will allow you to pass a WHERE clause.
Create a form ("PeriodsForm") based on the query Query1 and use something like the following code to open it:
Sub OpenPeriodForm()
DoCmd.OpenForm FormName:="PeriodsForm", View:=acFormDS, WhereCondition:="Period='Jul 2018'"
End Sub
Is there a way to create a button in Excel that runs a query, that is already created, in Access, and then updates the excel spreadsheet using the data from the query? I've searched the web for directions on how to do this, but have only found answers that create a button in Excel, that only runs a query in Excel, not Access. I am assuming this will be done by coding, upon click, in VBA, but have yet to find anything that does this. So... Is it possible? If so, how?
Okay, so I have kind of updated this with question, because I sort of used both options made. So I first created a Function in a Standard Module (Because we may use this later for another sheet in the workbook, and we didn't want to duplicate work):
Function GetSqlServerData(sQuery As String, sRange As Range)
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
' Create the connection string.
sConnString = "NMS"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Open the connection and execute.
conn.Open sConnString
Set rs = conn.Execute(sQuery)
' Check we have data.
If Not rs.EOF Then
' Transfer result.
Sheets(3).Range("A2").CopyFromRecordset rs
' Close the recordset
rs.Close
Else
MsgBox "Error: No records returned.", vbCritical
End If
' Clean up
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rs = Nothing
End Function
Then I tried to use said function:
Sub GetPermits()
Dim sQuery As String
Dim sRange As Range
Set sQuery = "Select * From Customer;"
Set sRange = Sheets(3).Range("A2")
GetSqlServerData(sQuery, sRange)
End Sub
But it gives me an error right at the spot where is actually use the function. I don't need it to go to a MsgBox, and I don't need it to print it out, all I need is for it to put in the data into the sheet noted on the function call. BTW, the function needs some tweeking, I know. Right now, I just need it to call the darn thing, lol.
Here is a Screen Shot of the error message: If you cant see it, it says, "Compile Error: Expected:=" and it highlights the "GetSqlServerData(sQuery, sRange)" in red. So it must have something to do with that. I just can't figure out what it is.
Screenshot of the error message
Dependent on your requirements, you could have this without VBA in a quicker and more reliable way, to have a table that is pointed at your query, that updates when you click Refresh.
To do so, in Excel navigate to Data > From Access.
From here, navigate to your database with the saved query, and when asked to select a table, you can select the query instead.
You can also write your own SQL query for Excel to execute against the database instead.
You can edit the connection properties to refresh when the workbook is opened, or refresh when every 60 minutes for example, or you could turn it all off, and allow the user to hit 'Refresh' in Excel itself.
Alternatively, you could setup a button that runs the refresh table command against the linked table, and this would do the same
Private Sub CommandButton1_Click()
ActiveWorkbook.RefreshAll
End Sub
Good luck.
As an example for a solution with VBA using ADODB one could use the following function to connect to the database.
Function ConnectToDB(ByVal fileName As String)
Dim conn As New ADODB.Connection
If Dir(fileName) = "" Then
MsgBox "Could not find file " & fileName
Exit Function
End If
Dim connectionString As String
' https://www.connectionstrings.com/access/
connectionString = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" _
& fileName & ";Persist Security Info=False;"
conn.Open connectionString
Set ConnectToDB = conn
End Function
And if you want to copy data from the database one could use the following code. You need to have a sheet with the codename shRepAllRecords
Option Explicit
Sub ReadFromDB()
' Get datbase name
Dim dbName As String
dbName = <fule filename of the database>
' Connect to the databse
Dim conn As ADODB.Connection
Set conn = ConnectToDB(dbName)
' read the data
Dim rs As New ADODB.Recordset
Dim query As String
' First example to use an SQL statement
' query = "SELECT * From CUSTOMER"
' Second example to use a query name defined in the database itself
query = "qryCustomer"
rs.Open query, conn
' shRepAllRecords is the codename of the sheet where the
' data is written to
' Write header
Dim i As Long
For i = 0 To rs.Fields.Count - 1
'shRepAllRecords.Cells(1, i + 1).Value = rs.Fields(i).Name
shRepAllRecords.Range("A1").Offset(0, i) = rs.Fields(i).Name
Next i
' Write Data
shRepAllRecords.Range("A2").CopyFromRecordset rs
shRepAllRecords.Activate
' clean up
conn.Close
End Sub
So I was finally able to figure out the issue. I was not putting "Call" in front of the function call. Once I did that, it accepted it. Thanks for all the assistance! The final answer was close to what Storax gave above. So I credited him with the answer. Thanks again!
I have code that looks like this:
If BLAH=BLAH
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Dim qdf As DAO.QueryDef
...rest of code...
'Close stuff
rst.Close
dbs.Close
Set dbs = Nothing
Set rst = Nothing
Set qdf = Nothing
END IF
If FOO=FOO
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Dim qdf As DAO.QueryDef
...rest of code...
'Close stuff
rst.Close
dbs.Close
Set dbs = Nothing
Set rst = Nothing
Set qdf = Nothing
END IF
But in the FOO=FOO section, it highlighted dbs As DAO.Database and gave a pop-up error message that says
Compile error: duplicate declaration in current scope.
What am I doing wrong? I thought this would be ok because I am negating each object before re-using. The overarching goal is to run a ton of IF statements on the Form Timer event. Do I need to maybe just declare some objects only once at the top of the event procedure to get around this error?
To clear this issue up in the case of VBA, variable declares are global to the given sub/function routine, or even to the given module.
For most here, the posters question seems strange, but keep in mind that in vb.net the variable definctions are LOCAL to a if/then block of code as posted.
This in VBA, this fails, but in vb.net, it is perfectally legal:
If True Then
Dim a As Long
a = 5
End If
If True Then
Dim a As Long
a = 6
End If
And if you wanted the variable able to be used in both if/then blocks, then you would use this:
Dim a As Long
If True Then
a = 5
End If
If True Then
a = 6
End If
So in VBA, variable scope is local to the function/subroutine. In vb.net the scope is local to the if/then block. This means in some programming languages you can declare a variable several times in a function/subroutine and not receive any compile errors – even when option explicit is in effect. I should also point out that in the above vb.net code examples when code drops out of the if/then block, then the given variable in the first code example goes out of scope. So in VBA, variable declares are not local to a if/then block. I should also point out that I used the word “local” scope, since the if/then code is compiled, and the if/then of the “dim” statement does NOT occur at runtime, but at compile time. In other words, "true" or "false" does not mean the "dim" and declare of the variable does not occur, but only that the variable is "local" to the if/then block. The dim statement does NOT run conditional and is created at compile time.
I am fetching a set of names from a database query and then reformatting it to a comma separated list. As I am using this functionallity a few Places in my app, I try to write it as a function getting the sql-query and returning the string.
Public Function String_from_query(StrSQL As String) As String
Dim dbs As Database
Dim rs As Recordset
Set dbs = CurrentDb
Dim results As String
results = ""
Set rs = dbs.OpenRecordset(StrSQL)
If Not (rs.EOF And rs.BOF) Then
rs.MoveFirst
Do While Not rs.EOF
If results > "" Then
results = results & ", "
End If
results = results & rs("Navn")
rs.MoveNext
Loop
End If
Set String_from_query = results
End Function
This is then called from an event handler:
Private Sub Detalj_Format(Cancel As Integer, FormatCount As Integer)
Dim StrSQL As String
StrSQL = "SELECT Personer.Navn FROM Personer INNER JOIN Personoppgaver ON Personer.Initialer = Personoppgaver.Initialer WHERE Personoppgaver.Oppgaveid =" & Me.Oppgaveid.Value
Me.Tekst52.Value = String_from_query(StrSQL)
End Sub
If I have the code from the String_from_query function within the event handler and then directly assigns Me.Tekst52 to results, everything works fine. When I refactor the code as shown, I get a "Compile Error, Object required" when I try to run it and a marker on the last line in the sub. (Set String_from_query = results). I am not able to see what is wrong. Any help?
The keyword Set is only required when assigning variables to an Object. For Access, this would be Forms, Reports, Recordsets, etc. Or other Objects outside of Access (FileSystemObject, for example).
When setting strings, dates, numbers, etc, you do not need Set.
You can surmise this from the error message as well, Object required.