This question already has answers here:
How do I use parameters in VBA in the different contexts in Microsoft Access?
(2 answers)
Closed 4 years ago.
I'm trying to better understand parameterized sql as a solution to SQL injection.
Lets say I have a tblCustomer with the fields CustName, Phone and Address. Lets also say I have an input form for new customers to enter their data, with controls called txtName, txtPhone and txtAddress.
I could run the following vba code:
dim strName, strPhone strAddress, strSQL as string
strName = me.txtName
strPhone = me.txtPhone
strAddress = me.txtAddress
strSQL = "INSERT INTO tblCustomer (CustName, Phone, Address) _
VALUES (" & strName & ", " & strPhone & ", " & strAddress & ");"
DoCmd.RunSQL strSQL
But then if someone nominated the address "Robert'); DROP TABLE tblCustomer; --" (wink) I'd have some serious problems.
I've used vba parameters, but they aren't helping me. So when people say use parameters to fix the issue, what do they mean?
Using a prepared statement with positional parameters eliminates the chance that someone may SQL inject you:
strSQL = "INSERT INTO tblCustomer (CustName, Phone, Address) " &
"VALUES ([str_name], [str_phone], [str_address]);"
Set qdf = db.CreateQueryDef(vbNullString, strSql)
With qdf
.Parameters("str_name").Value = strName
.Parameters("str_phone").Value = strPhone
.Parameters("str_address").Value = strAddress
.Execute dbFailOnError
End With
See here:
https://msdn.microsoft.com/en-us/library/office/ff845220.aspx
A longer winded explanation is that a parameterized query allows you to use a variable within your SQL query, and that variable will be properly escaped if it's a string or matched to your data type (for type checking), and prevent the problem that you've listed.
Related
Using the query builder in Access I am able to find the total, but I need to find the total using the vba code builder. The code given here gives me a null value.
Dim rst As DAO.Recordset
Dim dbs As Database
Dim strSQL As String
Set dbs = CurrentDb
strSQL = "SELECT Sum(GiftRcvd.Rcvdamount) AS SumOfRcvdamount FROM OurEvents INNER JOIN GiftRcvd ON OurEvents.EventName = GiftRcvd.EventName " & _
"WHERE ((([OurEvents].[EventDate])>" & Me.DateFrom.Value & " And ([OurEvents]![EventDate])< " & Me.DateTo.Value & "));"
Set rst = dbs.OpenRecordset(strSQL)
SumOfRcvdamount = rst![SumOfRcvdamount]
MsgBox SumOfRcvdamount
It's likely that your query is returning an empty recordset. Assuming you have data, this most likely means that your HAVING clause is filtering out the records you want.
As I remember, date literals in Access have to be in the format #1/30/2019#: a clause in the form [EventDate] > 1/30/2019 will not evaluate the way you want.
So try bracketing those date parameters with #:
[OurEvents].[EventDate])> "#" & Me.DateFrom.Value & "#"
Strictly speaking, you should avoid assembling queries from strings (due to the possibility of SQL Injection attacks): you should instead parameterize them and pass parameter values. BUT, that's harder to do in Access than in other forms of SQL.
You have to format your date values to valid string expressions:
"WHERE ((([OurEvents].[EventDate])> #" & Format(Me.DateFrom.Value, "yyyy\/mm\/dd") & "# And ([OurEvents]![EventDate])< #" & Format(Me.DateTo.Value, "yyyy\/mm\/dd") & "#));"
I'm using MS Access 2013. My current issue is with the following code, I use to log user activity.
Table is called: tbl-activitylog and has five columns :
id
timestamps
Username
Activity
Additional
I checked code many times char after char and don't know what's wrong :(
TempVars("UserName").Value = "admin"
Logging("Logon", "system")
Public Sub Logging(Activity, Additional As String)
Dim sql_code As String
sql_code = "INSERT INTO tbl-activitylog(Username, Activity, Additional) VALUES('" & TempVars("UserName").Value & "','" & Activity & "','" & Additional & "')"
Debug.Print sql_code
CurrentDb.Execute sql_code
End Sub
Debug print shows:
INSERT INTO tbl-activitylog(Username, Activity, Additional) VALUES('admin','Logon','System')
Becaus of using "-" you have to do it in this way [tbl-activitylog]
sql_code = "INSERT INTO [tbl-activitylog](Username, Activity, Additional) VALUES('" & TempVars("UserName").Value & "','" & Activity & "','" & Additional & "')"
This 3134 error denotes a syntax error in your INSERT statement. As the name of your table contains a dash, you need to enclose it between brackets :
INSERT INTO [tbl-activitylog]
(Username, Activity, Additional)
VALUES('admin','Logon','System')
Generally speaking you may as well enclose all fields and table names, to avoid all risks of clashes with ms-access reserved words, like :
INSERT INTO [tbl-activitylog]
([Username], [Activity], [Additional])
VALUES('admin','Logon','System')
Consider a parameterized query, an industry best practice in any application layer language running SQL in any database. With QueryDefs, you can parameterize queries in MS Access.
Even more MS Access will not allow you to save queries with syntax issues. So, be sure to escape special characters and reserved words with square brackets or backticks.
SQL (save below as a query object)
PARAMETERS UsernameParam Text, ActivityParam Text, AdditionalParam Text;
INSERT INTO [tbl-activitylog] ([Username], [Activity], [Additional])
VALUES ([UsernameParam], [ActivityParam], [AdditionalParam])
VBA (reference above query and bind values without quotes or concatenation)
TempVars("UserName").Value = "admin"
Logging("Logon", "system")
Public Sub Logging(Activity, Additional As String)
Dim sql_code As String
Dim qdef As QueryDef
Set qdef = CurrentDb.QueryDefs("mySavedQuery")
' BIND PARAMS
qdef![UsernameParam] = TempVars("UserName")
qdef![ActivityParam] = Activity
qdef![AdditionalParam] = Additional
qdef.Execute dbFailOnError
Set qdef = Nothing
End Sub
I am trying to run the following SQL statement in ACCESS 2013 VBA but am getting errors due to wrong formatting (in this case I get "Semicolon (;) missing from end of statement"). Could anybody tell me what I am doing wrong in the code below please?
Dim dbs As dao.Database
Set dbs = CurrentDb()
dbs.Execute "INSERT INTO TEMP2 ([Study_Date], [Created_By], [Part_Number],
[Upper_Tolerance], [Lower_Tolerance], [ID21_Number]) VALUES ([Study_Date],
[Created_By], [Part_Number], [Upper_Tolerance], [Lower_Tolerance], [ID21_Number])
FROM RAC_DATA_ENTRY
WHERE [RAC_CAP_VALS] = '" & Me.[RAC_CAP_VALS] & "'"
Don't use VALUES when you're pulling data from one table to INSERT into another. Use SELECT instead.
This example uses just two of your fields. Add in the others you need.
Dim strInsert As String
strInsert = "INSERT INTO TEMP2 ([Study_Date], [Created_By])" & _
" SELECT [Study_Date], [Created_By] FROM RAC_DATA_ENTRY" & _
" WHERE [RAC_CAP_VALS] = '" & Me.[RAC_CAP_VALS].Value & "';"
Debug.Print strInsert '<- view this in Immediate window; Ctrl+g will take you there
dbs.Execute strInsert, dbFailOnError
Notes:
A semicolon at the end of the statement is optional. Access will consider the statement valid with or without it.
Value is not actually required following Me.[RAC_CAP_VALS], since it's the default property. I prefer to make it explicit.
dbFailOnError gives you better information about failed inserts. Without it, a problem such as a primary key violation would fail silently.
Debug.Print strInsert allows you to inspect the statement you built and are asking the db engine to execute. If there is a problem, you can copy the statement text from the Immediate window and paste it into SQL View of a new Access query for testing.
A friend and I have been trying for hours with little progress to a get a piece of code right for an invoicing system we're designing as a project.
We are trying to update the field InvoiceNo to a value (worked out earlier in the VisualBasic code), where the CustomerNo is the is a specific value and the FinishDate is between two dates. At first I was trying to use TO_DATE but then we realized that wasn't the same in the SQL that Access uses (after much searching).
This has been the simple statement I've been using to just test and try to get something working to then translate into VisualBasic and put in our variables. It's a little easier to read so I thought I'd provide it.
UPDATE tblJob SET tblJob.InvoiceNo = '8' WHERE tblJob.CustomerNo = '1' AND (tblJob.FinishDate BETWEEN cdate(format('08/09/2013', '##/##/####')) AND cdate(format('03/10/2013', '##/##/####')));
I have a feeling after looking at a few examples that our date is meant to be without an forward slashes. So I tried that and it wasn't working either.
Here's the VisualBasic code that has come out of all of this, it's exactly the same but using some variables rather than our set values that I've been using for testing.
DoCmd.RunSQL ("UPDATE tblJob SET tblJob.InvoiceNo = '" & newInvoiceNo & "' WHERE tblJob.CustomerNo = '" & VbCustNo & "' AND (tblJob.FinishDate BETWEEN cdate(format('" & Forms![frmMainMenu][txtFirstDate] & "', '##/##/####')) AND cdate(format('" & Forms![frmMainmenu][txtEndDate] & "', '##/##/####')));")
We had a look at: Convert a string to a date in Access and it helped us realize that it was cdate(format()) rather than TO_DATE as it is in Oracle. But we just can't seem to get it to run properly, any help would be much appreciated.
If you will be running the query from within an Access application session, you can let the db engine use the Access expression service to grab the values from the text boxes on your form.
Dim db As DAO.Database
Dim strUpdate As String
strUpdate = "UPDATE tblJob" & vbCrLf & _
"SET InvoiceNo = '" & newInvoiceNo & "'" & vbCrLf & _
"WHERE CustomerNo = '" & VbCustNo & "'" & vbCrLf & _
"AND FinishDate BETWEEN Forms!frmMainMenu!txtFirstDate AND Forms!frmMainmenu!txtEndDate;"
Debug.Print strUpdate
Set db = CurrentDb
db.Execute strUpdate, dbFailOnError
Set db = Nothing
However, if you prefer to build the literal date values from those text boxes into your UPDATE statement, you can use Format().
"AND FinishDate BETWEEN " & _
Format(Forms!frmMainmenu!txtFirstDate, "\#yyyy-m-d\#") & _
" AND " & Format(Forms!frmMainmenu!txtEndDate, "\#yyyy-m-d\#") & ";"
Either way, using a string variable to hold your UPDATE statement gives you an opportunity to examine the completed statement you're asking the db engine to execute.
You can view the output from Debug.Print in the Immediate window (go there with Ctl+g). For troubleshooting, you can copy the statement text from there and then paste it into SQL View of a new Access query.
I'm using MS Access 2003 and I'm trying to execute a few queries at once using VB. When I write out the query in SQL it works fine, but when I try to do it in VB it asks me to "Enter Parameter Value" for DEPA, then DND (which are the first few letters of a two strings I have). Here's the code:
Option Compare Database
Public Sub RemoveDupelicateDepartments()
Dim oldID As String
Dim newID As String
Dim sqlStatement As String
oldID = "DND-01"
newID = "DEPA-04"
sqlStatement = "UPDATE [Clean student table] SET [HomeDepartment]=" & newID & " WHERE [HomeDepartment]=" & oldID & ";"
DoCmd.RunSQL sqlStatement & ""
End Sub
It looks to me as though it's taking in the string up to the - then nothing else. I dunno, that's why I'm asking lol. What should my code look like?
Use (') character to set start and end of value
sqlStatement = "UPDATE [Clean student
table] SET [HomeDepartment]='" & newID
& "' WHERE [HomeDepartment]='" & oldID
& "';"
You probably want to insert quotes around the IDs.