How to use a VBA function in a query - vba

I have a Module called GetRole I want to use the returned string variable from the Function in the WHERE clause of the Query.
Please see the Query and the Function below. I get undefined function error when I try to run the query.
My question is, what is the correct way of using a function in a query? Thanks and kind regards!
The Query:
SELECT * FROM tblUsers WHERE Role = GetRole();
In the GetRole module:
Option Compare Database
Option Explicit
Public Function GetRole() As String
Dim rs As DAO.Recordset
Dim strUserName As String
Dim strUsers As Object
Set strUsers = CreateObject("WScript.Network")
strUserName = strUsers.UserName
Set rs = CurrentDb.OpenRecordset("SELECT [Role] FROM tblUsers WHERE [UserName] Like '" & strUserName & "'")
GetRole = rs.Fields("Role").Value
Set strUsers = Nothing
rs.Close
Set rs = Nothing
End Function

Don't give modules and functions the same name - the module name will override the function name, hence the undefined function error.
Name the module e.g. mGetRole, and it should work.

Related

Access - Reference a Access Form unbound textbox in TSQL Server Query? [duplicate]

I have Access front end which is powered by SQL Server in backend.
I made a form which requires user input, based on the input it should run a pass through query and should display result in the subform. I have read on internet this cannot be done through sql command and it requires sql coding to be wrapped in VBA coding.
I have made a code this like this:
Private Sub id_AfterUpdate()
Dim MyDb As Database, MyQry As QueryDef
Set MyDb = CurrentDb()
Set MyQry = MyDb.CreateQueryDef("")
MyQry.Connect = "ODBC;DSN=mikecollections;UID=***;PWD=****;DATABASE=mikecollections;"
MyQry.SQL = "select currency.tb_coins.id,documenttype,documentsubtype,documentname" & _
"from currency.tb_coins" & _
"inner join collectibles.tb_documents on tb_coins.id=collectibles.tb_documents.prodid" & _
"where currency.tb_coins.id=[forms]![test_form]![id]"
End Sub
This code should fire after I enter value in the id field in the form, but nothing happens. I do not know how to make this code work. Im new to SQL and VBA, Pls help!
I created a stored procedure in sql server and created a parameter in access to pass into sql server and got it working. Thank you Hansup and iDevlop for all the help.
Private Sub userinput_AfterUpdate()
Dim qrypass As DAO.QueryDef
Dim rst As DAO.Recordset
Set qrypass = CurrentDb.QueryDefs("qu_test")
qrypass.SQL = _
"exec collectibles.sp_coinsvsdocuments " _
& "#userinput=" _
& Forms!test_form!userinput
End Sub
You logically can't refer to an Access object in a passthru query.
So your sql should pass the value of the control, instead of referencing the control itself.
If id is a long:
MyQry.SQL = "select currency.tb_coins.id,documenttype,documentsubtype,documentname" & _
"from currency.tb_coins" & _
"inner join collectibles.tb_documents on tb_coins.id=collectibles.tb_documents.prodid" & _
"where currency.tb_coins.id= " & [forms]![test_form]![id]
Then you need to add the code to assign that querydef to a recordset, in order to read it. Something like:
set rs = myQry.OpenRecordset
If you prefer, you can also name your queryDef. it will then be available like any other query:
Set MyQry = MyDb.CreateQueryDef("someName")
'''specify all properties here...
doCmd.OpenQuery "someName"
Here is a solution for all Microsoft Access
just copy the below code into a Microsoft Access Module and change the parameter
Sub test()
' The "Kim_ZZ_Orderlines" has a string 'arg1' in the sql so we just replace this
' with the new value
RunPassThrough "KIM_ZZ_Orderlines", "Indput value", "10"
End Sub
Sub RunPassThrough(QueryToRun, ArgPrompt, ArgDefault)
' John Henriksen
' 2021-09-23
' Sub procedure to run a Pass-through query with argument
' in the query there must be the string 'Arg1" you want to replace
' e.g. AND APPS.OE_ORDER_LINES_ALL.ATTRIBUTE1 = 'arg1'
arg1 = VBA.InputBox(ArgPrompt, ArgPrompt, ArgDefault)
Dim db As DAO.Database
Dim qry, qryNy As String
Dim qrydefOrginal As DAO.QueryDef
Set db = CurrentDb
Set qdef = db.QueryDefs(QueryToRun)
' Save the original sql
qry = qdef.sql
' Make a new sql, here we search for the string "arg1" and replace with our arg1 value
qryNy = VBA.Replace(qry, "arg1", arg1)
' Set the new sql
qdef.sql = qryNy
DoCmd.OpenQuery QueryToRun
' Set the original sql
qdef.sql = qry
Set qdef = Nothing
Set db = Nothing
End Sub

Tracking down an invalid procedure call error in a SQL VBA execute command

Thanks for opening this. I've been wracking my brain for the past couple of days trying to make heads of tails of this error (Run-time error '5': Invalid procedure call) error that has blocked me at every turn.
Essentially, my goal is to run and execute a SQL command to find the largest number in a given category and store it into a variable for later use. I've tried different ways to go about it and no matter how I do it (either in VBA using a DMAX or through SQL) I run into the same error whenever I try to execute the command. I've gotten some help working through it but I think there's some deeper issue that I am not understanding with VBA.
Here is the code:
Public Function GetMaxValue(Child As String)
Dim RAC As DAO.RecordSet
Dim Prefix As String
Dim MaxVal As Long
Dim SearchString As String
NewPrefix = Child
SearchString = "SELECT MAX([SalesValue]) FROM [SalesTable] WHERE [Prefix] = '" & NewPrefix & "';"
Set RAC = CurrentDb.OpenRecordset(SearchString)
If RAC.Fields.Count = 1 Then
MaxVal = RAC.Fields(0)
End If
RAC.Close
Set RAC = Nothing
End Function
It breaks whenever I hit the line that reads Set RAC = CurrentDb... with:
Invalid procedure call error
Please let me know if anyone has any idea what produces this error. I've searched everywhere for a possible explanation and I can't find anything that would cause my code to break whenever I try to run a max function. I even made sure that the SalesValue was a Number field in the underlying Access table and that everything was spelled correctly.
Thanks!
You can get the error
Invalid Procedure Call
on line
CurrentDb.OpenRecordset(SearchString)
just because your SearchString does not contain a valid SQL query.
Never use string concatenation to pass parameters to a query.
This is bad: WHERE [Prefix] =
'" & NewPrefix & "';"
See this answer and parametrize your query.
See this documentation.
First create a query definition:
Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim strSQL As String
Set qdf = dbs.CreateQueryDef("qrySearchQuery")
Application.RefreshDatabaseWindow
strSQL = "PARAMETERS NewPrefix TEXT"
strSQL = strSQL & "SELECT MAX([SalesValue]) FROM [SalesTable] "
strSQL = strSQL & "WHERE [Prefix] = [NewPrefix];"
qdf.SQL = strSQL
qdf.Close
Set qdf = Nothing
Then you can call it:
NewPrefix = Child
Dim rst As DAO.Recordset
Set qfd = dbs.QueryDefs("qrySearchQuery")
qdf.Parameters("NewPrefix") = NewPrefix
Set rst = qdf.OpenRecordset()
You could use DMax:
Public Function GetMaxValue(Child As String)
GetMaxValue = DMax("[SalesValue]", "[SalesTable]", "[Prefix] = '" & Child & "'")
End Function

Pass through query with userinput in access form

I have Access front end which is powered by SQL Server in backend.
I made a form which requires user input, based on the input it should run a pass through query and should display result in the subform. I have read on internet this cannot be done through sql command and it requires sql coding to be wrapped in VBA coding.
I have made a code this like this:
Private Sub id_AfterUpdate()
Dim MyDb As Database, MyQry As QueryDef
Set MyDb = CurrentDb()
Set MyQry = MyDb.CreateQueryDef("")
MyQry.Connect = "ODBC;DSN=mikecollections;UID=***;PWD=****;DATABASE=mikecollections;"
MyQry.SQL = "select currency.tb_coins.id,documenttype,documentsubtype,documentname" & _
"from currency.tb_coins" & _
"inner join collectibles.tb_documents on tb_coins.id=collectibles.tb_documents.prodid" & _
"where currency.tb_coins.id=[forms]![test_form]![id]"
End Sub
This code should fire after I enter value in the id field in the form, but nothing happens. I do not know how to make this code work. Im new to SQL and VBA, Pls help!
I created a stored procedure in sql server and created a parameter in access to pass into sql server and got it working. Thank you Hansup and iDevlop for all the help.
Private Sub userinput_AfterUpdate()
Dim qrypass As DAO.QueryDef
Dim rst As DAO.Recordset
Set qrypass = CurrentDb.QueryDefs("qu_test")
qrypass.SQL = _
"exec collectibles.sp_coinsvsdocuments " _
& "#userinput=" _
& Forms!test_form!userinput
End Sub
You logically can't refer to an Access object in a passthru query.
So your sql should pass the value of the control, instead of referencing the control itself.
If id is a long:
MyQry.SQL = "select currency.tb_coins.id,documenttype,documentsubtype,documentname" & _
"from currency.tb_coins" & _
"inner join collectibles.tb_documents on tb_coins.id=collectibles.tb_documents.prodid" & _
"where currency.tb_coins.id= " & [forms]![test_form]![id]
Then you need to add the code to assign that querydef to a recordset, in order to read it. Something like:
set rs = myQry.OpenRecordset
If you prefer, you can also name your queryDef. it will then be available like any other query:
Set MyQry = MyDb.CreateQueryDef("someName")
'''specify all properties here...
doCmd.OpenQuery "someName"
Here is a solution for all Microsoft Access
just copy the below code into a Microsoft Access Module and change the parameter
Sub test()
' The "Kim_ZZ_Orderlines" has a string 'arg1' in the sql so we just replace this
' with the new value
RunPassThrough "KIM_ZZ_Orderlines", "Indput value", "10"
End Sub
Sub RunPassThrough(QueryToRun, ArgPrompt, ArgDefault)
' John Henriksen
' 2021-09-23
' Sub procedure to run a Pass-through query with argument
' in the query there must be the string 'Arg1" you want to replace
' e.g. AND APPS.OE_ORDER_LINES_ALL.ATTRIBUTE1 = 'arg1'
arg1 = VBA.InputBox(ArgPrompt, ArgPrompt, ArgDefault)
Dim db As DAO.Database
Dim qry, qryNy As String
Dim qrydefOrginal As DAO.QueryDef
Set db = CurrentDb
Set qdef = db.QueryDefs(QueryToRun)
' Save the original sql
qry = qdef.sql
' Make a new sql, here we search for the string "arg1" and replace with our arg1 value
qryNy = VBA.Replace(qry, "arg1", arg1)
' Set the new sql
qdef.sql = qryNy
DoCmd.OpenQuery QueryToRun
' Set the original sql
qdef.sql = qry
Set qdef = Nothing
Set db = Nothing
End Sub

How to pass a SQL query result to variable with VBA Access

I need to create a standalone function which checks whether a value is true or false. I have attempted to set up a SQL query that does this, but can't get the result to pass to the variable.
The query will always return only one record.
How can I pass the result of the SQL string as the returning value of the function?
UPDATED
Private Function HasBM(iMandate As Variant) As Boolean
'Returns boolean value for whether or not mandate has a benchmark
Dim sSQL1 As String
Dim sSQL2 As String
Dim db As Database
Dim rs As Recordset
sSQL1 = "SELECT tbl_Mandate_Type.BM_Included " & _
"FROM tbl_Mandate_Type INNER JOIN tbl_MoPo_BM ON tbl_Mandate_Type.Mandate_Type_ID = tbl_MoPo_BM.MandateType_ID " & _
"WHERE (((tbl_MoPo_BM.MoPo_BM_ID)="
sSQL2 = "));"
Set db = CurrentDb
Set rs = db.Execute(sSQL1 & iMandate & sSQL2)
HasBM = rs.Fields(0).Value
End Function
UPDATE 2: SOLUTION
Thanks for Magisch and HansUp I ended up getting two solutions that work:
DLOOKUP Solution by Magisch as I implemented it:
Private Function HasBM2(iMandate As Variant)
'Returns boolean value for whether or not mandate has a benchmark
Dim tmp As String
tmp = CStr(DLookup("tbl_MoPo_BM.MandateType_ID", "tbl_MoPo_BM", "tbl_MoPo_BM.MoPo_BM_ID = " & iMandate))
HasBM2 = DLookup("tbl_Mandate_Type.BM_Included", "tbl_Mandate_Type", "Mandate_Type_ID =" & tmp)
End Function
Second Solution using recordset as HansUp helped create:
Private Function HasBM(iMandate As Variant) As Boolean
'Returns boolean value for whether or not mandate has a benchmark
Dim sSQL1 As String
Dim sSQL2 As String
Dim db As Database
Dim rs As dao.Recordset
sSQL1 = "SELECT tbl_Mandate_Type.BM_Included " & _
"FROM tbl_Mandate_Type INNER JOIN tbl_MoPo_BM ON tbl_Mandate_Type.Mandate_Type_ID = tbl_MoPo_BM.MandateType_ID " & _
"WHERE (((tbl_MoPo_BM.MoPo_BM_ID)="
sSQL2 = "));"
Set db = CurrentDb
Set rs = db.OpenRecordset(sSQL1 & iMandate & sSQL2)
HasBM = rs.Fields(0).Value
rs.close
End Function
You can use the DLookup function native to access for this. Since you have an INNER JOIN in your SQL, you will have to use it twice, but it'll still work.
Use a temporary string variable to store the temporary output, like this:
Dim tmp as String
tmp = Cstr(DLookup("tbl_MoPo_BM.MandateType_ID","tbl_MoPo_BM","tbl_MoPo_BM.MoPo_BM_ID = " & iMandate)) 'This is to fetch the ID you performed the inner join on
HasBM = DLookup("tbl_Mandate_Type.BM_Included","tbl_Mandate_Type","Mandate_Type_ID =" & tmp) ' this is to get your value
Using your DoCmd.RunSQL will not suffice since its for action queries only (INSERT, DELETE, UPDATE) and incapable of returning the result.
Alternatively, you can use a Recordset with your SQL query to fetch what you want, but thats more work and the DLookup is designed to exactly avoid doing that for single column return selects.

VBA OpenRecordset Producing Too few parameters. Expected 2. Error

I have a query called qryAlloc_Source that has two paramaters under one criteria:
>=[forms]![frmReportingMain]![txtAllocStart] And <=[forms]![frmReportingMain]![txtAllocEnd])
A have a separate query that ultimately references qryAlloc_Source (there are a couple queries in between), and that query runs fine when I double click it in the UI, but if I try to open it in VBA, I get an error. My code is:
Dim rst As Recordset
Set rst = CurrentDb.OpenRecordset("qryAlloc_Debits")
I am getting run-time error 3061, Too few parameters. Expected 2. I've read that I may need to build out the SQL in VBA using the form parameters, but it would be pretty complex SQL given that there are a few queries in the chain.
Any suggestions as to a workaround? I considered using VBA to create a table from the query and then just referencing that table--I hate to make extra steps though.
The reason you get the error when you just try to open the recordset is that your form is not open and when you try to access [forms]![frmReportingMain] it's null then you try to get a property on that null reference and things blow up. The OpenRecordset function has no way of poping up a dialog box to prompt for user inputs like the UI does if it gets this error.
You can change your query to use parameters that are not bound to a form
yourTableAllocStart >= pAllocStart
and yourTableAllocEnd <= pAllocEnd
Then you can use this function to get the recordset of that query.
Function GetQryAllocDebits(pAllocStart As String, pAllocEnd As String) As DAO.Recordset
Dim db As DAO.Database
Dim qdef As DAO.QueryDef
Set db = CurrentDb
Set qdef = db.QueryDefs("qryAlloc_Debits")
qdef.Parameters.Refresh
qdef.Parameters("pAllocStart").Value = pAllocStart
qdef.Parameters("pAllocEnd").Value = pAllocEnd
Set GetQryAllocDebits = qdef.OpenRecordset
End Function
The disadvantage to this is that when you call this now on a form that is bound to it it doesn't dynamically 'fill in the blanks' for you.
In that case you can bind forms qryAlloc_debts and have no where clause on the saved query, then use the forms Filter to make your where clause. In that instance you can use your where clause exactly how you have it written.
Then if you want to still open a recordset you can do it like this
Function GetQryAllocDebits(pAllocStart As String, pAllocEnd As String) As DAO.Recordset
Dim qdef As DAO.QueryDef
Set qdef = New DAO.QueryDef
qdef.SQL = "Select * from qryAlloc_Debits where AllocStart >= pAllocStart and pAllocEnd <= pAllocEnd"
qdef.Parameters.Refresh
qdef.Parameters("pAllocStart").Value = pAllocStart
qdef.Parameters("pAllocEnd").Value = pAllocEnd
Set GetQryAllocDebits = qdef.OpenRecordset
End Function
While a [Forms]!... reference does default to a form reference when a QueryDef is run from the GUI, it is actually just another Parameter in the query in VBA. The upshot is you don't have to recode your query/create a new one at all. Also, as #Brad mentioned, whether a parameter is in the final query of a chain of queries or not, you are able to refer to the parameter as if it is in the collection of the final query. That being the case, you should be able to use code similar to this:
Sub GetQryAllocDebits(dteAllocStart As Date, dteAllocEnd as Date)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rst As DAO.Recordset
Set db = CurrentDb()
Set qdf = db.QueryDefs("qryAlloc_Debit")
If CurrentProject.AllForms("frmReportingMain").IsLoaded Then
qdf.Parameters("[forms]![frmReportingMain]![txtAllocStart]") = [forms]![frmReportingMain]![txtAllocStart]
qdf.Parameters("[forms]![frmReportingMain]![txtAllocEnd]") = [forms]![frmReportingMain]![txtAllocEnd]
Else
qdf.Parameters("[forms]![frmReportingMain]![txtAllocStart]") = CStr(dteAllocStart)
qdf.Parameters("[forms]![frmReportingMain]![txtAllocEnd]") = CStr(dteAllocEnd)
End If
Set rst = qdf.OpenRecordset
Do Until rst.EOF
'...do stuff here.
Loop
Set rst = Nothing
Set qdf = Nothing
Set db = Nothing
End Function
If the referenced form is open, the code is smart enough to use the referenced controls on the form. If not, it will use the dates supplied to the subroutine as parameters. A gotcha here is that the parameters did not like when I set them as date types (#xx/xx/xx#), even if the field were dates. It only seemed to work properly if I set the params as strings. It didn't seem to be an issue when pulling the values straight out of the controls on the forms, though.
I know it's been a while since this was posted, but I'd like to throw in my tuppence worth as I'm always searching this problem:
A stored query can be resolved:
Set db = CurrentDb
Set qdf = db.QueryDefs(sQueryName)
For Each prm In qdf.Parameters
prm.Value = Eval(prm.Name)
Next prm
Set rst = qdf.OpenRecordset
For SQL:
Set db = CurrentDb
Set qdf = db.CreateQueryDef("", "SELECT * FROM MyTable " & _
"WHERE ID = " & Me.lstID & _
" AND dWeekCommencing = " & CDbl(Me.frm_SomeForm.Controls("txtWkCommencing")) & _
" AND DB_Status = 'Used'")
For Each prm In qdf.Parameters
prm.Value = Eval(prm.Name)
Next prm
Set rst = qdf.OpenRecordset
This assumes that all parameter values are accessible - i.e. forms are open and controls have values.
'I have two parameters in my recordset and I was getting the "Too few parameters. Expected 2" 'error when using an OpenRecordset in MS Access vba, and this is how I got around it and IT WORKS! see the below sub routine:
'Private Sub DisplayID_Click()
'1. I created variables for my two parameter fields xEventID and xExID as seen below:
Dim db As Database
Dim rst As Recordset
Dim xEventID As Integer
Dim xExId As Integer
'2. Sets the variables to the parameter fields as seen below:
Set db = CurrentDb
xEventID = Forms!frmExhibitorEntry!txtEventID
xExId = Forms!frmExhibitorEntry!subExhibitors!ExID
'3. Set the rst to OpenRecordSet and assign the Set the variables to the WHERE clause. Be sure to include all quotations, ampersand, and spaces exactly the way it is displayed. Otherwise the code will break!exactly as it is seen below:
Set rst = db.OpenRecordset("SELECT tblInfo_Exhibitor.EventID,tblInfo_Display.ExID, tblMstr_DisplayItems.Display " _
& "FROM tblInfo_Exhibitor INNER JOIN (tblMstr_DisplayItems INNER JOIN tblInfo_Display ON tblMstr_DisplayItems.DisplayID = tblInfo_Display.DisplayID) ON tblInfo_Exhibitor.ExID = tblInfo_Display.ExID " _
& "WHERE (((tblInfo_Exhibitor.EventID) =" & xEventID & " ) and ((tblInfo_Exhibitor.ExID) =" & xExId & " ));")
rst.Close
Set rst = Nothing
db.Close
'End Sub