I am trying to create a Parameter Query in Access 2007 VBA that takes two input parameters from user forms and also returns a derived field value based on the public function RESULT I have written elsewhere in the code. The function determines the value of a calculated "Result" field which is derived from the values of 4 fields in the underlying table, [HomeTeam],[AwayTeam],[HomeGoals],[AwayGoals].
If I debug the code through the immediate window and paste the SQL string into the Access SQL window the query runs perfectly but will not run from VBA. The error generated is:
"Run-time error 3141. The SELECT statement includes a reserved word or an argument name that is misspelled or missing, or the punctuation is incorrect."
I do not want to run the query directly in Access as the Parameter [season] is a table variable.
Does anybody know if and how I can call a user defined function in VBA within a SQL string?
I am unsure if this is just a syntax problem.
Code:
Sub sqlSeason()
Dim db As DAO.Database
Dim qdef As DAO.QueryDef
Dim season As String
Dim team As String
Dim strSQL As String
Set db = CurrentDb()
DoCmd.Close acQuery, "qrySeason"
db.QueryDefs.Delete "qrySeason"
season = [Forms]![frmSeason]![comboSeason]
strSQL = "PARAMETERS [Forms]![frmClubProfile]![comboTeam] Text (255), [Forms]![frmSeason]![comboSeason] Text (255) ;" & _
"SELECT [Date], [HomeTeam], [AwayTeam], [HomeGoals], [AwayGoals], Result([HomeTeam], [AwayTeam], [HomeGoals], [AwayGoals]) AS Result" & _
"FROM [" & [season] & "]" & _
"GROUP BY [Date], [HomeTeam], [AwayTeam], [HomeGoals], [AwayGoals], Result([HomeTeam], [AwayTeam], [HomeGoals], [AwayGoals])" & _
"HAVING ((([" & [season] & "].HomeTeam) = [Forms]![frmClubProfile]![comboTeam])) Or ((([" & [season] & "].AwayTeam) = [Forms]![frmClubProfile]![comboTeam]))" & _
"ORDER BY [Date];"
Debug.Print strSQL
Set qdef = db.CreateQueryDef("qrySeason", strSQL)
DoCmd.OpenQuery "qrySeason"
End Sub
Function:
Public Function Result(ByRef HomeTeam As String, ByRef AwayTeam As String, ByRef HomeGoals As Integer, ByRef AwayGoals As Integer) As String
team = [Forms]![frmClubProfile]![comboTeam]
If team = HomeTeam And HomeGoals > AwayGoals Then
result = "Home Win"
End If
If team = HomeTeam And HomeGoals = AwayGoals Then
result = "Home Draw"
End If
If team = HomeTeam And HomeGoals < AwayGoals Then
result = "Home Lost"
End If
If team = AwayTeam And HomeGoals < AwayGoals Then
result = "Away Win"
End If
If team = AwayTeam And HomeGoals = AwayGoals Then
result = "Away Draw"
End If
If team = AwayTeam And HomeGoals > AwayGoals Then
result = "Away Lost"
End If
End Function
Why does this work in Access but not in VBA?
Any help would be appreciated.
Try this to expand your form values inside of the string before it is evaluated:
"SELECT [Date], [HomeTeam], [AwayTeam], [HomeGoals], [AwayGoals], Result([HomeTeam], [AwayTeam], [HomeGoals], [AwayGoals]) AS Result" & _
"FROM [" & [season] & "]" & _
"GROUP BY [Date], [HomeTeam], [AwayTeam], [HomeGoals], [AwayGoals], Result([HomeTeam], [AwayTeam], [HomeGoals], [AwayGoals])" & _
"HAVING ((([" & [season] & "].HomeTeam) = '" & [Forms]![frmClubProfile]![comboTeam] & "'")) Or ((([" & [season] & "].AwayTeam) = '" & [Forms]![frmClubProfile]![comboTeam] & "'))" & _
"ORDER BY [Date];"
Value being expanded:
[Forms]![frmClubProfile]![comboTeam]
Assuming comboTeam is a string, if it is numeric, skip the single quotes.
Related
This is my code below. I am trying to search a database using two different dates, company name
I am getting an error when one of the date fields is empty or null. How can I solve this issue or bypass if the date search field is empty to ignore it in the search or search for an empty field?
Dim SQL As String
SQL = "SELECT * from qryRequestInternal where ([DateRequestSent] = #" & txt_Search_Sdate & "# AND [DateReceived] = #" & txt_Search_Rdate & "# AND (companyName like ""*" & txt_SCompNa & "*"") )"
Me.sfrmRequestInternal.Form.RecordSource = SQL
Me.sfrmRequestInternal.Form.Requery
Me.sfrmRequestInternal_col.Form.RecordSource = SQL
Me.sfrmRequestInternal_col.Form.Requery
End Sub
You will need to check for Null values and build the SQL string based on the controls which have a value (i.e. not null).
The example below uses a helper function to build the sql string. If nothing is inserted, it will only run the the Select * from qryRequestInternal without any criteria.
Private Function SqlWhere() As String
Dim retvalue As String
'sent date
With txt_Search_Sdate
If Not IsNull(.Value) Then
retvalue = " WHERE [DateRequestSent] = #" & Format(.Value, "mm/dd/yyyy") & "#"
End If
End With
'received date
With txt_Search_Rdate
If Not IsNull(.Value) Then
retvalue = IIf(Len(retvalue) = 0, " WHERE", retvalue & " AND") & " [DateReceived] = #" & Format(.Value, "mm/dd/yyyy") & "#"
End If
End With
'company
With txt_SCompNa
If Not IsNull(.Value) Then
retvalue = IIf(Len(retvalue) = 0, " WHERE", retvalue & " AND") & " [companyName] Like '*" & .Value & "*'"
End If
End With
SqlWhere = retvalue
End Function
To call it:
Dim sqlString As String
sqlString = "SELECT * from qryRequestInternal" & SqlWhere()
Debug.Print sqlString
Private Sub Form_Close()
Dim sSQL, stringSQL As String
Dim rst As DAO.Recordset
sSQL = "SELECT BarCode, [Goods Name] FROM tblInventory WHERE BarCode='" & Me.ID & "'"
Set rst = CurrentDb.OpenRecordset(sSQL)
If rst.EOF Then
stringSQL = "INSERT INTO tblInventory(BarCode,[Goods Name],Unit,[Unit Price],[Initial Stock],[Current Stock],[Exit Item]) values('" & Me.ID & "','" & Me.GoodsName & "','" & Me.Unit & "'," & Replace(Format(Me.Price, "0.00"), ",", ".") & "," & Me.Amount & "," & Me.Amount & ",0)"
DoCmd.SetWarnings False
DoCmd.RunSQL [stringSQL]
DoCmd.SetWarnings True
Else
stringSQL = "UPDATE tblInventory SET [Current Stock]=[Current Stock]+" & Me.Amount & " WHERE BarCode='" & Me.ID & "'"
DoCmd.SetWarnings False
DoCmd.RunSQL (stringSQL)
DoCmd.SetWarnings True
End If
rst.Close
End Sub
Firstly, note that this:
Dim sSQL, stringSQL As String
Results in sSQL being defined as a Variant, not a String; which, although will not cause your code to fail (since a Variant can hold data of any type), it will be memory inefficient.
Instead, you should use:
Dim sSQL As String, stringSQL As String
Or, perhaps more readable:
Dim sSQL As String
Dim stringSQL As String
Secondly, when invoking a function independently of any other expression, you should not surround the arguments with parentheses of any type.
In your code, on line 11 you have:
DoCmd.RunSQL [stringSQL]
And on line 16 you have:
DoCmd.RunSQL (stringSQL)
Both of these should be changed to:
DoCmd.RunSQL stringSQL
Or, just supply the SQL string directly, e.g.:
DoCmd.SetWarnings False
DoCmd.RunSQL "UPDATE tblInventory SET [Current Stock]=[Current Stock]+" & Me.Amount & " WHERE BarCode='" & Me.ID & "'"
DoCmd.SetWarnings True
Also, since you are only using the recordset to test whether a record exists, you could simplify this to a DLookup call, e.g.:
DLookup("BarCode","tblInventory","BarCode=Forms![YourForm]!ID")
And test whether or not this returns Null using IsNull:
If IsNull(DLookup("BarCode", "tblInventory", "BarCode=Forms![YourForm]!ID")) Then
...
Else
...
End If
Finally, it is much better practice to use a parameterised query in place of concatenating values within a SQL statement.
Using parameters offers two key advantages:
Protects against SQL injection.
Automatically handles SQL data types.
For example, consider the following code:
Private Sub Form_Close()
If IsNull(DLookup("BarCode", "tblInventory", "BarCode=Forms![YourForm]!ID")) Then
With CurrentDb.CreateQueryDef _
( _
"", _
"insert into tblInventory(BarCode,[Goods Name],Unit,[Unit Price],[Initial Stock],[Current Stock],[Exit Item]) " & _
"values(#id, #goodsname, #unit, #unitprice, #initstock, #stock, 0)" _
)
.Parameters(0) = Me.ID
.Parameters(1) = Me.GoodsName
.Parameters(2) = Me.Unit
.Parameters(3) = Replace(Format(Me.Price, "0.00"), ",", ".")
.Parameters(4) = Me.Amount
.Parameters(5) = Me.Amount
.Execute
End With
Else
With CurrentDb.CreateQueryDef _
( _
"", _
"update tblInventory set [Current Stock]=[Current Stock]+#amount where BarCode=#id" _
)
.Parameters(0) = Me.Amount
.Parameters(1) = Me.ID
.Execute
End With
End If
End Sub
Try manually to run the SQL with some values.
You probably need to use either parameters or to concatenate the variables properly, for example using my function CSql.
I have a query that gives me the Current Employee Rate based on the WorkDay:
(Select Top 1 T1.Rate FROM tblERates as T1
WHERE T1.EMPID = tblPayroll.EMPID And T1.EffectiveDate <= tblPayroll.WorkDay
ORDER BY T1.EMPID asc, T1.EffectiveDate desc)
The project is growing. I need to return the Employee Rate on different Forms/Reports so I want to convert this SQL to a Public Function.
I tried this:
Public Function fncERate(EID As Integer, WD As Date) As Double
Dim intERate As Double
Dim intWD As String
intWD = "#" & Format(WD, "m/d/yyyy") & "#"
intERate = "SELECT TOP 1 Rate" _
& "FROM tblERates" _
& "WHERE EMPID = EID And EffectiveDate <= intWD" _
& "ORDER BY EMPID asc;"
fncERate = intERate
End Function
I get a "type mismatch error".
After tinkering a bit I came up with this:
Public Function fncERate(EID As Integer, WD As Date) As Double
Dim intERate As String
Dim intWD As String
Dim intEID As Integer
intWD = "#" & Format(WD, "m/d/yyyy") & "#"
intERate = "SELECT TOP 1 [Rate]" & _
" FROM tblERates" & _
" WHERE [EMPID] = " & EID & " And [EffectiveDate] <= " & intWD & " " & _
" ORDER BY [EMPID] asc;"
With CurrentDb.OpenRecordset(intERate)
If Not (.BOF And .EOF) Then
fncERate = .Fields(0)
End If
End With
End Function
Yes you lack spaces in your sql syntax
A little tip for quick dev
Press Ctrl+G to open execution pane while debugging and type
?intERate
to print the value of your variable
then you can just copy paste the sql and try it directly
I am working on a group project at school and need to be able to export data from an Access table to an excel document that is formatted with specific headings (for Sage import). I am having trouble with the "exportItems" function.
Everytime I run my code, it will create duplicate rows in excel. For example: On export, it exports 1 item that a customer ordered. If I export it again to a different file name and location, that item will be duplicated and the excel document contains 2 instances. It then becomes 3, and then 4, if I repeat those steps.
Any help would be appreciated. I feel like it is an error somewhere in my SQL Insert Statement.
Private Sub cmdConfirmExport_Click()
If Not IsNull(txtInputFile.Value) Or txtInputFile.Value = "" Then
Dim exportID As Integer
Dim insertOESQL, insertOEDSQL, FileName, DeleteSQL, nameInput, rsSQL As String
exportID = Form_QuoteDetail.QuotesID
nameInput = txtInputFile.Value
exportOrders (exportID)
' To insert the order details and be able to get the line number,
' create a function similar to exportCommentsOE that will use a recordset to get the values for
'ORDUNIQ(QuotesID FK), ITEM(Vendor Item Num), DESC(Item Description), QTYORDERED(Quantity), QTYBACKORD(Quantity),
'UNITPRICE(Unit Price), UNITCOST(Unit Cost)
' Then with While (Not .EOF), Insert individual item records using rs.Fields([FieldName]). LINENUM
' will be added as a value from a counter that goes up by 32 until it reaches the end of the recordset.
exportItems (exportID)
exportCommentsOE (exportID)
FileName = FolderSelection & "\" & "OE_" & nameInput & "_" & Format(Date, "yyyyMMdd") & ".xlsx"
'FileName = CurrentProject.Path & "\" & "OE_" & nameInput & "_" & Format(Date, "yyyyMMdd") & ".xlsx"
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "Orders", FileName, True
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "Order_Details", FileName, True
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "Order_Detail_Lot_Numbers", FileName, True
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "Order_Detail_Serial_Numbers", FileName, True
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "Order_Payment_Schedules", FileName, True
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "Order_Comments_Instructions", FileName, True
MsgBox "Export Complete", vbOKOnly, "Success"
DeleteSQL = "DELETE * FROM Orders WHERE NOT ORDUNIQ = " & exportID
CurrentDb.Execute DeleteSQL
DoCmd.Close
Else
MsgBox "You must input a file name.", vbOKOnly, "Invalid Input"
End If
End Sub
Function exportItems(itemQuoteID As Integer)
On Error GoTo exportError
Dim rsSQL, rsINSERTSQL As String
Dim rs As DAO.Recordset
Dim rslineCount As Integer
Dim rsORDUNIQ, rsQTYO, rsQTYBO As Integer
Dim rsUP, rsUC As Currency
Dim rsDesc, rsITEM, rsHC As String
Set rs = Nothing
rslineCount = 32
rsSQL = "SELECT [QuotesID FK], [Vendor Item Num], [Item Description], [Quantity], [Unit Price], [Unit Cost], [HASCOMMENT] FROM [Quote Detail]" _
& "WHERE [QuotesID FK] = " & itemQuoteID
Set rs = CurrentDb.OpenRecordset(rsSQL)
With rs
.MoveLast
.MoveFirst
While (Not .EOF)
rsORDUNIQ = .Fields("[QuotesID FK]")
rsITEM = .Fields("[Vendor Item Num]")
rsDesc = .Fields("[Item Description]")
rsQTYO = .Fields("[Quantity]")
rsQTYBO = .Fields("[Quantity]")
rsUP = .Fields("[Unit Price]")
rsUC = .Fields("[Unit Cost]")
rsHC = .Fields("[HASCOMMENT]")
rsINSERTSQL = "INSERT INTO Order_Details(ORDUNIQ, [LINENUM], [ITEM], [DESC], [QTYORDERED], [QTYBACKORD], [UNITPRICE], [UNITCOST], [COMMINST])" _
& "VALUES (" & rsORDUNIQ & "," & rslineCount & ", '" & rsITEM & "', '" & rsDesc & "'," & rsQTYO & "," & rsQTYBO & "," & rsUP & "," & rsUC _
& ", '" & rsHC & "')"
CurrentDb.Execute rsINSERTSQL
rslineCount = rslineCount + 32
.MoveNext
Wend
.Close
End With
Set rs = Nothing
ExitFunction:
Set rs = Nothing
Exit Function
exportError:
MsgBox "An error has occured during export of items."
Resume ExitFunction
End Function
Figured out the reason. The actual SQL Insert statement continued to create entries in the DB. This would all be exported to the Excel Spreadsheet making it look like it was "appended" to the previous document.
Solution was just to make tables (Orders would have a parallel table called Orders_Export as an example) and use it as a middle man. The INSERT Command would insert data into both tables and the export would only export data from the Orders_Export table. The Orders_Export table was then cleared but all data remained in the Orders table. This was the work around I implemented. Thanks to all that helped!
I have a form with several checkboxes in it. Values of ticking checkboxes are used after in the SQL-query including in Excel-macro. I use these values in SQL "IN"-operator. So, everythig works. But I don't like the code for my macro.
For ticking checkboxes I use such code (and if there were more value the list would be very huge):
Public Location1 As String
Public Location2 As String
Public Location3 As String
Public Location4 As String
Private Sub OKCommandButton2_Click()
If CheckBox1.Value = True Then Location1 = "LocationValue1"
If CheckBox2.Value = True Then Location2 = "LocationValue2"
If CheckBox3.Value = True Then Location3 = "LocationValue3"
If CheckBox4.Value = True Then Location4 = "LocationValue4"
...
And for using it in SQl I use such code:
query = "SELECT Param1, Param2, Param3, Param4, 0, 0, Param5, 0 FROM Table1 " & _
"WHERE Param1 like'" & "%" & CraftDefinition.Craft & "%" & "'AND Param6>0 AND Param2 IN ('" & _
LocationDefinition.Location1 & "','" & LocationDefinition.Location2 & "','" & LocationDefinition.Location3 & "','" & _
LocationDefinition.Location4 & "')" & _
"ORDER BY Param2, Param3"
The question is: can I rewrite my code in more compact, laconic and sophisticated way? Maybe I should use another operator in SQL-part; maybe I can rewrite my VBA-part, for using only one parameter in SQl.
Thank you.
you can use the "controls" Feature and write your Value into the "TAG" from the checkbox
Dim TB As Control
Dim ChkBoxString As String
ChkBoxString = "("
For Each TB In Me.Controls
If TypeOf TB Is CheckBox Then
If TB.Value = True Then
ChkBoxString = ChkBoxString & TB.Tag & ", "
End If
End If
Next TB
ChkBoxString = ChkBoxString & ")"
ChkBoxString = Replace(ChkBoxString, ", )", ")")
So you can use your Script:
query = "SELECT Param1, Param2, Param3, Param4, 0, 0, Param5, 0 FROM Table1 " & _
"WHERE Param1 like'" & "%" & CraftDefinition.Craft & "%" & "'AND Param6>0 AND Param2 " _
IN " & ChkBoxString
Greets Ralf
Create function that return string expression with comma delimited, and set the value in the Tag property of the checkbox
Function GetExpression() As String
Dim contr As Control
Dim comma As String
Dim str As String
str = ""
For Each contr In UserForm1.Controls
comma = IIf(str <> "", ",", "")
If TypeName(contr) = "CheckBox" Then
If contr.Value = True Then str = str + comma + contr.Tag
End If
Next
GetExpression = str
End Function
If the sub that "makes" the query (or build the query string) is called while the Userform is still loaded then you can code as follows:
Option Explicit
Dim Locations As String '<--| UserForm scoped variable: all subs in the userform code pane can "see" it
Private Sub OKCommandButton2_Click()
Dim ctrl As Control
For Each ctrl In Me.Controls '<--| loop through userform controls
If TypeName(ctrl) = "CheckBox" Then '<--| consider only checkboxes
If ctrl.value Then Locations = Locations & "LocationValue" & Mid(ctrl.Name, 9, Len(ctrl.Name) - 8) & "','" '<--| if checkbox is checked then update 'Location' string
End If
Next ctrl
End Sub
Private Sub QuerySub() '<-- name of any Sub inside Userfom code pane that has to make the query
Dim Query As String
If Locations <> "" Then '<--| this Sub can "see" 'Locations' even if it has been initialized in a different Sub of the same Userform code pane
Query = "SELECT Param1, Param2, Param3, Param4, 0, 0, Param5, 0 FROM Table1 " & _
"WHERE Param1 like'" & "%" & CraftDefinition.Craft & "%" & "'AND Param6>0 AND Param2 IN ('" & _
Left(Locations, Len(Locations) - 3) & "')" & _
"ORDER BY Param2, Param3"
Else
' code to handle 'Locations' empty string
End If
End Sub
This seems a tiny bit wrong because you get IN ('', '', '', '') when unchecked, but if you don't mind that, then maybe something like this:
locations$ = Mid$( _
IIf(CheckBox1, "','LocationValue1", "") & _
IIf(CheckBox2, "','LocationValue2", "") & _
IIf(CheckBox3, "','LocationValue3", "") & _
IIf(CheckBox4, "','LocationValue4", ""), 4) ' works even is all unchecked
query = " ... AND Param2 IN ('" & locations & "') ORDER BY Param2, Param3"
or if all of the values really start with "LocationValue" then
Locations$ = Mid$(Replace(IIf(CheckBox1, ",1", "") & _
IIf(CheckBox2, ",2", "") & IIf(CheckBox3, ",3", "") & _
IIf(CheckBox4, ",4", ""), ",", "','LocationValue"), 4)