I am trying to sort out some VBA in Access today with very limited knowledge. I have constructed the code below for a simple insert into a table within the same access database.
I am getting the error
"Missing semicolon (;) at end of SQL statement."
However I have the semicolon in there. I can only assume I have made a rookie error with syntax somewhere but after spending far too long trying I am stumped, any pointers much appreciated!
Dim StrSQL As String
Dim InDate As Date
Dim VacNum As String
Dim Stage As String
InDate = Now()
VacNum = Me.Vacancy_No
Stage = Me.Current_Stage
StrSQL = "INSERT INTO Stage (Vacancy_ID,StageDate,Stage) VALUES ('" & VacNum & "','" & InDate & "','" & Stage & "' );"
DoCmd.SetWarnings False
DoCmd.RunSQL StrSQL
DoCmd.SetWarnings True
Your syntax is at risk for SQL injection, and that's probably why you're getting this message.
The likely cause of this error is that one of your field contains a single quote, making the SQL malfunction.
The proper solution to this is to parameterize:
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Set db = CurrentDb
Set qdf = db.CreateQueryDef("", "INSERT INTO Stage (Vacancy_ID,StageDate,Stage) VALUES (#Value1,#Value2,#Value3 );")
qdf.Parameters("#Value1") = VacNum
qdf.Parameters("#Value2") = InDate
qdf.Parameters("#Value3") = Stage
qdf.Execute
Related
I have a new database to help produce documents for order processing.
I have a query set up with only one column of results (Product Codes) determined by the order selected on the main form.
I need to be able to use this information to name my file aka
(Customer) (Product1)+(Product2)+(Product....) (Location)
I have the code to generate the line (Customer) (Product1) (Location) and am trying to get either a concatenate function or a loop function or something to get all the products to line up with a "+" in between each "line".
I have a query (Query1) set up to give me the exact data I need...
SELECT tblREF_Chemical.Abbr
FROM qry_AX_LineItems_DISTINCT INNER JOIN tblREF_Chemical ON
qry_AX_LineItems_DISTINCT.ItemId = tblREF_Chemical.[Item Number]
GROUP BY tblREF_Chemical.Abbr, qry_AX_LineItems_DISTINCT.SALESID,
tblREF_Chemical.[Proper Shipping Name]
HAVING (((qry_AX_LineItems_DISTINCT.SALESID)=[Forms]![frm_SalesOrderEntry]!
[Combo617]) AND ((tblREF_Chemical.[Proper Shipping Name]) Is Not Null));
I have a button set up on my main form to test the data output and then I intend to add the code to my code for DoCmd.Output file name.
So far the only code that has worked is...
Private Sub Command1492_Click()
Dim i As Integer
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim SQL As String
Set db = CurrentDb
SL = [Forms]![frm_SalesOrderEntry]![Combo617]
SQL = "SELECT * FROM ALL_SalesOrderItemsLineDates WHERE
ALL_SalesOrderItemsLineDates.SALESID = '" & SL & "';"
Set rst = db.OpenRecordset(SQL)
For i = 0 To DCount("*", "ALL_SalesOrderItemsLineDates",
"ALL_SalesOrderItemsLineDates.SALESID = '" & [Forms]![frm_SalesOrderEntry]!
[Combo617] & "'") - 1
Debug.Print DLookup("[Abbr]", "[tblREF_Chemical]", "[Item Number]= '" &
rst.Fields("ItemID") & "'")
rst.MoveNext
Next i
rst.Close
End Sub
I can't seem to add additional where statements within this code or use my actual query or the system presents errors at the db.OpenRecordset line of code (Errors 3061 and 3078).
Even ignoring those problems the output is multi-line and I need it to be used in a single string of text for the document name.
UPDATE1:
I am working with the code to use my query directly...
Dim i As Integer
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim SQL As String
Set db = CurrentDb
SL = [Forms]![frm_SalesOrderEntry]![Combo617]
SQL = "SELECT tblREF_Chemical.Abbr "
SQL = SQL & "FROM qry_AX_LineItems_DISTINCT INNER JOIN tblREF_Chemical ON qry_AX_LineItems_DISTINCT.ItemId = tblREF_Chemical.[Item Number] "
SQL = SQL & "GROUP BY tblREF_Chemical.Abbr, qry_AX_LineItems_DISTINCT.SALESID, tblREF_Chemical.[Proper Shipping Name] "
SQL = SQL & "HAVING ((qry_AX_LineItems_DISTINCT.SALESID)='" & SL & "'"
SQL = SQL & "AND ((tblREF_Chemical.[Proper Shipping Name]) Is Not Null)); "
Set rst = db.OpenRecordset(SQL)
Dim s As String
Do While rst(0) Is Not Null
s = s & "+" & rst(0)
rst.MoveNext
Loop
rst.Close
Debug.Print s
Unfortunately I'm now getting a run-time error 3061 - Too few parameters. Expected 1.
I have double checked my spellings and ran the query just to be sure and no matter how many results the query is getting (functioning as expected) I am still getting this error.
UPDATE2:
Through more research I learned that queries can have, for lack of better words, invisible coding. I am updating my code to remove the inner query from my query to simplify the amount of "research" my VBA has to do.
Private Sub Command1492_Click()
Dim i As Integer
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim SQL As String
Set db = CurrentDb
SL = [Forms]![frm_SalesOrderEntry]![Combo617]
SQL = "SELECT tblREF_Chemical.Abbr "
SQL = SQL & "FROM ALL_SalesOrderItemsLineDates INNER JOIN tblREF_Chemical ON ALL_SalesOrderItemsLineDates.ItemId = tblREF_Chemical.[Item Number] "
SQL = SQL & "GROUP BY tblREF_Chemical.Abbr, ALL_SalesOrderItemsLineDates.SALESID, tblREF_Chemical.[Proper Shipping Name]"
SQL = SQL & "HAVING ((ALL_SalesOrderItemsLineDates.SALESID)='" & SL & "'"
SQL = SQL & "AND ((tblREF_Chemical.[Proper Shipping Name]) Is Not Null)); "
Set rst = db.OpenRecordset(SQL)
Dim s As String
Do While rst(0) Is Not Null 'Debug error here!
s = s & "+" & rst(0)
rst.MoveNext
Loop
rst.Close
Debug.Print s
End Sub
Unfortunately I'm still getting a run-time error, but now it is 424 Object required and the debug takes me to the "Do While" line.
I think this is a step forward, but still a little stuck.
Update3:
Since the debug was taking me to the "Do While" line I returned to my functioning code and replaced the loop function with an integer based code.
Thank you #Harassed Dad! Your code was a giant help! Using your idea for a string rather than going straight to a debug.print was genius.
The below replaces my code starting where I was having issues.
Dim s As String
For i = 0 To DCount("*", "ALL_SalesOrderItemsLineDates", "ALL_SalesOrderItemsLineDates.SALESID = '" & SL & "'") - 1
s = s & "+" & rst.Fields("Abbr")
rst.MoveNext
Next i
rst.Close
Debug.Print s
My results are displaying with only one hiccup.
+CHA+DEEA+EEP+MEC+PERC+PM+PROP
There is an extra "+" at the beginning, but I'm sure I can find the solution to this tiny problem.
I hope these notes can help someone in the future. Thank you all for your help!
Private Sub Command1492_Click()
Dim i As Integer
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim SQL As String
Set db = CurrentDb
SL = [Forms]![frm_SalesOrderEntry]![Combo617]
SQL = "SELECT tblREF_Chemical.Abbr "
SQL = SQL & "FROM qry_AX_LineItems_DISTINCT INNER JOIN tblREF_Chemical ON "
SQL = SQL & "qry_AX_LineItems_DISTINCT.ItemId = tblREF_Chemical.[Item Number] "
SQL = SQL & "GROUP BY tblREF_Chemical.Abbr, qry_AX_LineItems_DISTINCT.SALESID, "
SQL = SQL & "tblREF_Chemical.[Proper Shipping Name] "
SQL = SQL & "HAVING (((qry_AX_LineItems_DISTINCT.SALESID)='" & SL & "'" 'edit here
SQL = SQL & "AND ((tblREF_Chemical.[Proper Shipping Name]) Is Not Null)); "
Set rst = db.OpenRecordset(SQL)
Dim s as string
Do While rst(0) is not null
s = s & "+" & rst(0)
rst.MoveNext
Loop
rst.Close
Debug.print s
End Sub
Your main issue is a missing space before the AND in HAVING clause.
For this very reason of readability and maintainability, consider using QueryDefs for parameterized queries (an industry best practice) to run your saved query in VBA for several reasons:
You avoid the need to concatenate or enclose quotes or escape literals by effectively divorcing SQL code from VBA (application layer) code.
MS Access will not allow you to save a query with syntax issues but VBA string queries can have such issues found at runtime.
MS Access's engine compiles and caches saved queries to best execution plan which especially helps for aggregate queries with joins. This is why saved queries are usually more efficient than VBA string queries run on the fly.
SQL (save below as a saved query)
Query now uses table aliases and HAVING conditions are moved to WHERE since no aggregate is being used.
PARAMETERS idparam LONG;
SELECT t.Abbr
FROM qry_AX_LineItems_DISTINCT q
INNER JOIN tblREF_Chemical t ON q.ItemId = t.[Item Number]
WHERE (((q.SALESID) = [idparam])
AND ((t.[Proper Shipping Name]) Is Not Null))
GROUP BY t.Abbr, q.SALESID, t.[Proper Shipping Name];
VBA
Dim db As DAO.Database, qdef AS DAO.QueryDef, rst As DAO.Recordset
Dim SQL As String, s As String
Set db = CurrentDb
' INITIALIZE SAVED QUERY
Set qdef = db.QueryDefs("mySavedQuery")
' BIND PARAMETER
qdef![idparam] = [Forms]![frm_SalesOrderEntry]![Combo617]
' OPEN RECORDSET
Set rst = qdef.OpenRecordset()
Do While rst(0) Is Not Null
s = s & "+" & rst(0)
rst.MoveNext
Loop
rst.Close
Debug.Print s
Set rst = Nothing: Set qdef = Nothing: Set db = Nothing
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
I have an Access form which contains an insert statement.
The code works very good, but the problem when I enter a text that contains a single quote ':
strSQL = "INSERT INTO student (stname) VALUES ('" & stname.Value & "')"
DoCmd.RunSQL strSQL
when stname.value = Ra'ed or ka'l
then statement becomes wrong
Generally I would have to agree with marc_s regarding the SQL injection warning. However for a quick and dirty solution on a non-mission critical db that you don't mind other users potentially hacking into or otherwise messing with, you would use
strSQL = "INSERT INTO student (stname) VALUES ('" & Replace (stname.Value, "'", "''") & "')"
Consider a DAO parameterized query which allows binding values without quote enclosure. Access queries allows the PARAMETERS clause which can be binded with external values:
Dim db As Database
Dim qdef As QueryDef
Dim strSQL As String
Set db = CurrentDb
' PREPARE STATEMENT (STRING CAN BE A SAVED QUERY)
strSQL = "PARAMETERS [strValParam] Text(255);" _
& " INSERT INTO student (stname) VALUES ([strValParam]);"
' INITIALIZE QUERYDEF OBJECT (REPLACE EMPTY STRING "" FOR SAVED QUERY NAME AND NO STRSQL)
Set qdef = db.CreateQueryDef("", strSQL)
' BIND VALUE (SINGLE/DOUBLE/SPECIAL CHARS ALL ALLOWED)
qdef!strValParam = stname.Value
' EXECUTE ACTION QUERY
qdef.Execute
Set qdef = Nothing
Set db = Nothing
I'm trying to update an access query using VBA and changing the Like criteria in the SQL statement, but although I know that SQL code works, when running with VBA it continues to return the same information.
My code is below:
Function FTPCost()
Dim database As DAO.database
Dim query As DAO.QueryDef
Dim strSQL As String
Set database = CurrentDb
Set query = database.QueryDefs("ftp_for_a_part_Query")
strSQL = "SELECT ftp_for_a_part.PART, ftp_for_a_part.STD_TOT, ftp_for_a_part.DATE " & _
"FROM ftp_for_a_part " & _
"WHERE (((ftp_for_a_part.PART) Like '******')) " & _
"ORDER BY ftp_for_a_part.DATE DESC;"
'MsgBox strSQL
query.SQL = strSQL
DoCmd.OpenQuery "ftp_for_a_part_Query"
Set query = Nothing
Set database = Nothing
End Function
Solved my problem. The fact the SQL code was not updating the Query was being caused by the query window being open. I did not understand that for the query to run the Query window must be closed.
I am using the code below to create a new record in the "transactions table" the second line of the insert statement is throwing an error: Too few parameters. I have double checked and all of the field names are correct. What else could cause this type of error?
' Modify this line to include the path to Northwind
' on your computer.
Set dbs = CurrentDb
Dim vblCustomerID As String
Dim vblMealType As String
Dim Charge As Currency
Dim vblDate As String
vblDate = Format(Date, "yyyy-mm-dd")
txtCustomerID.SetFocus
vblCustomerID = txtCustomerID.Text
txtMealType.SetFocus
vblMealType = txtMealType.Text
txtCharge.SetFocus
vblCharge = txtCharge.Text
dbs.Execute "INSERT INTO dbo_Transactions" _
& "(CustomerID, MealID, TransactionAmount, TransactionDate) VALUES " _
& "(" & vblCustomerID & ", " & vblMealType & ", " & vblCharge & ", " & vblDate & ");"
dbs.Close
As others have suggested, using a parameterized query is a much better way of doing what you're attempting to do. Try something like this:
Dim qdf As DAO.QueryDef
Set qdf = dbs.CreateQueryDef("", _
"PARAMETERS prmCustomerID Long, prmMealID Long, prmTransactionAmount Currency, prmTransactionDate DateTime;" & _
"INSERT INTO dbo_Transactions (CustomerID, MealID, TransactionAmount, TransactionDate) " & _
"VALUES ([prmCustomerID], [prmMealID], [prmTransactionAmount], [prmTransactionDate]) ")
qdf!prmCustomerID = txtCustomerID.Value
qdf!prmMealID = txtMealType.Value
qdf!prmTransactionAmount = txtCharge.Value
qdf!prmTransactionDate = Date()
qdf.Execute dbFailOnError
Set qdf = nothing
Do any of the text fields you're loading into your vbl fields contain special characters like these?
, ' "
All of those in a text field in a perfectly good SQL Insert command could screw things up, I bet that's what happening here.
It would be better if you actually use parameters here to, rather than loading the text in textboxes directly into your SQL queries, since you're opening yourself up to SQL Injections. What if someone types
"; Drop Table dbo_Transactions;
in one of your textboxes and you run this query? Your database is then totally screwed up because someone just deleted one of your tables.
A few links to info on using Parameters to prevent this issue, which I'll bet will also fix the too few parameters issue you're having.
http://forums.asp.net/t/886691.aspx
http://sqlmag.com/blog/t-sql-parameters-and-variables-basics-and-best-practices