BaldyWeb
A Tutorial on Building SQL in VBA
How do I construct an SQL statement in VBA?
Constructing an SQL statement in VBA can be tricky, because there are often
several areas of Access needed to construct the string. For example, proper SQL
syntax, proper VBA syntax, and proper referencing of controls are three common
points. We will cover the basics of constructing SQL statements in this FAQ, and
touch on some incidental points as well.
String concatenation
This is perhaps the most common problem area. You must include proper spacing
and delimiting.
--Spacing--
Consider:
Public Function SQLTest()
Dim strSQL As String
strSQL = "SELECT * FROM tblMain" & _
"ORDER BY [TableID]"
CurrentDb.OpenRecordset strSQL
End Function
When attempting to execute this procedure, we will get a run-time error. To help
identify the problem, modify the code slightly to print out the SQL string to
the immediate window. This is the single most helpful debugging technique for
troubleshooting SQL statements in VBA:
Public Function SQLTest()
Dim strSQL As String
strSQL = "SELECT * FROM tblMain" & _
"ORDER BY [TableID]"
Debug.Print strSQL
CurrentDb.OpenRecordset strSQL
End Function
Now, take a look at the output (in the immediate window):
SELECT * FROM tblMainORDER BY [TableID]
Easy to see that you are missing a space between the table name and the ORDER BY
clause. Simple enough to fix:
strSQL = "SELECT * FROM tblMain " & _
"ORDER BY [TableID]"
Note the insertion of a space immediately after tblMain. This is a common
mistake, especially when breaking out a SQL statement over multiple lines. Some
like to put a space at the end of the line, some like to put a space at the
beginning of the next line. This doesn't matter, just be consistent.
--Delimiting--
Certain types of data need certain types of delimiters to be handled properly.
Numbers, strings, and dates are each handled differently. We will start with
strings, as they are the most complicated:
Consider:
Public Function SQLTest()
Dim strSQL As String
Dim strMyString As String
strMyString = "jason"
strSQL = "SELECT * FROM tblMain " & _
"WHERE [TextField] = strMyString"
Debug.Print strSQL
CurrentDb.OpenRecordset strSQL
End Function
When attempting to open this recordset we will get a runtime error. To see why,
let's look at the resulting SQL statement:
SELECT * FROM tblMain WHERE [TextField] = strMyString
Of course, this is not what we want. Where is the string "jason" in the
above? It is not there because we included the variable name strMyString inside
the quotation marks, so the variable name was treated as literal name and was
not evaluated for what it contained.
To get this value, we need to put the variable outside of the SQL string, so
that VBA can evaluate the string and then include that evaluated value into the
SQL string. Let's try:
strSQL = "SELECT * FROM tblMain " & _
"WHERE [TextField] = " & strMyString
When attempting to open this recordset, we get the same error! Why? Well, look
at the resulting SQL statement:
SELECT * FROM tblMain WHERE [TextField] = jason
The problem here is just a bit more subtle. In the above statement jason
is not being treated as a string. We need to surround it with a delimiter that
SQL knows means "a string". Like quotation marks (single or double). This is
where it starts getting confusing:
strSQL = "SELECT * FROM tblMain " & _
"WHERE [TextField] = " & Chr$(39) & strMyString & Chr$(39)
This is fine (for now). Look at the SQL statement:
SELECT * FROM tblMain WHERE [TextField] = 'jason'
Chr$(39) is the single quotation mark (or apostrophe).
Here's the thing: What if the string contains a delimiter? That is, what if the
name was "O'Toole"?
strMyString = "O'Toole"
strSQL = "SELECT * FROM tblMain " & _
"WHERE [TextField] = " & Chr$(39) & strMyString & Chr$(39)
Another error! Look at the resulting SQL statement:
SELECT * FROM tblMain WHERE [TextField] = 'O'Toole'
This is a problem of surprising complexity if you have to deal with strings that
contain possible delimiters. You could delimit the string with double quotation
marks:
strSQL = "SELECT * FROM tblMain " & _
"WHERE [TextField] = " & Chr$(34) & strMyString & Chr$(34)
SELECT * FROM tblMain WHERE [TextField] = "O'Toole"
Chr$(34) is the double quotation mark.
This is ok. But what if the string itself contains a double quotation mark?
strMyString = Chr$(34) & "What now?" & Chr$(34)
SELECT * FROM tblMain WHERE [TextField] = ""What now?""
An error! What to do? Well, you could double up the delimiting quotation marks:
strSQL = "SELECT * FROM tblMain " & _
"WHERE [TextField] = " & Chr$(34) & Chr$(34) & _
strMyString & Chr$(34) & Chr$(34)
SELECT * FROM tblMain WHERE [TextField] = """What now?"""
This works, but isn't easy to read, and even worse fails if the string does not
contain double quotation marks! The solution some like is to use to always use
either single quotes or double quotes for delimiters. Then, use a function to
"prepare" the string for quoting, which simply means going through the string
and doubling the chosen delimiter. The doubled-up delimiter is now treated as a
normal character. There is also more reading on this issue at the Knowledge Base: HOWTO:
Query for Literal Special Characters in a Where Clause
You'll be glad to know that if you made it through all that, the hardest part is
behind you! Dates are up next, and we're going to look at the right way to
handle them, and introduce a few new points about SQL in VBA as incidental
information:
Public Function SQLTest()
Dim strSQL As String
Dim dtmMyDate As Date
Dim rs As DAO.Recordset
dtmMyDate = "1/1/2000"
strSQL = "SELECT * FROM tblMain " & _
"WHERE [DateField] = #" & dtmMyDate & "#"
Debug.Print strSQL
Set rs = CurrentDb.OpenRecordset(strSQL)
If Not (rs.EOF) Then
Debug.Print rs![DateField]
Else
Debug.Print "Nothing found"
End If
Set rs = Nothing
End Function
The output:
SELECT * FROM tblMain WHERE [DateField] = #1/1/2000#
1/1/2000
Easy enough. We use octothorpes to delimit dates. You can try the above without
delimiters and string-style delimiters to see the results. It's good to do this
so you can understand the error or invalid results in case you make a mistake in
the future.
The incidental points: We're opening the recordset and storing it in the
variable rs. We are using rs.EOF to verify we got back something.
This would be True if there were no matches, and if there were no matches, the recordset
would be empty, and then we would get an error trying to print out rs![DateField].
Non-USA Date Formats
Another tricky things to remember about dates is that Jet expects all dates to
be in the format mm/dd/yyyy. It does not take any international date format
settings in the Control Panel into account. This means if you are storing your
dates in a format like dd.mm.yy , you still need to use the mm/dd/yyyy format
for dates in SQL.
Finally, we saved the easiest for last -- numbers. Now numbers are real easy,
because you don't need a delimiter for them:
Public Function SQLTest()
Dim strSQL As String
Dim lngMyNumber As Long
Dim rs As DAO.Recordset
lngMyNumber = 30
strSQL = "SELECT * FROM tblMain WHERE [NumberField] = " & lngMyNumber
Debug.Print strSQL
Set rs = CurrentDb.OpenRecordset(strSQL)
If Not (rs.EOF) Then
Debug.Print rs![Textfield]
Else
Debug.Print "Nothing found"
End If
Set rs = Nothing
End Function
SELECT * FROM tblMain WHERE [NumberField] = 30
tom
Really straight forward. The incidental point: note I am printing a different
field than I used in my criteria. That's because I'm selecting all fields using
the asterisk in the SELECT clause.
That's the basics of delimiting data for SQL statements. Next up, we will cover
referencing values from form controls, and provide examples of more complex SQL
statements to build a sample search form in the sample database below.
JasonM