Escaping ' in Access SQL - sql

I'm trying to do a domain lookup in vba with something like this:
DLookup("island", "villages", "village = '" & txtVillage & "'")
This works fine until txtVillage is something like Dillon's Bay, when the apostrophe is taken to be a single quote, and I get a run-time error.
I've written a trivial function that escapes single quotes - it replaces "'" with "''". This seems to be something that comes up fairly often, but I can't find any reference to a built-in function that does the same. Have I missed something?

The "Replace" function should do the trick. Based on your code above:
DLookup("island", "villages", "village = '" & Replace(txtVillage, "'", "''") & "'")

It's worse than you think. Think about what would happen if someone entered a value like this, and you haven't escaped anything:
'); DROP TABLE [YourTable]
Not pretty.
The reason there's no built in function to simply escape an apostrophe is because the correct way to handle this is to use query parameters. For an Ole/Access style query you'd set this as your query string:
DLookup("island", "village", "village = ? ")
And then set the parameter separately. I don't know how you go about setting the parameter value from vba, though.

Though the shorthand domain functions such as DLookup are tempting, they have their disadvantages. The equivalent Jet SQL is something like
SELECT FIRST(island)
FROM villages
WHERE village = ?;
If you have more than one matching candidate it will pick the 'first' one, the definition of 'first' is implementation (SQL engine) dependent and undefined for the Jet/ACE engine IIRC. Do you know which one would be first? If you don’t then steer clear of DLookup :)
[For interest, the answer for Jet/ACE will either be the minimum value based on the clusterd index at the time the database file was last compacted or the first (valid time) inserted value if the database has never been compacted. Clustered index is in turn determined by the PRIAMRY KEY if persent otherwise a UNIQUE constraint or index defined on NOT NULL columns, otherwise the first (valid time) inserted row. What if there is more than one UNIQUE constraint or index defined on NOT NULL columns, which one would be used for clustering? I've no idea! I trust you get the idea that 'first' is not easy to determine, even when you know how!]
I've also seen advice from Microsoft to avoid using domain aggregate functions from an optimization point of view:
Information about query performance in an Access database
http://support.microsoft.com/kb/209126
"Avoid using domain aggregate functions, such as the DLookup function... the Jet database engine cannot optimize queries that use domain aggregate functions"
If you choose to re-write using a query you can then take advantage of the PARAMETERS syntax, or you may prefer the Jet 4.0/ACE PROCEDURE syntax e.g. something like
CREATE PROCEDURE GetUniqueIslandName
(
:village_name VARCHAR(60)
)
AS
SELECT V1.island_name
FROM Villages AS V1
WHERE V1.village_name = :village_name
AND EXISTS
(
SELECT V2.village_name
FROM Villages AS V2
WHERE V2.village_name = V1.village_name
GROUP
BY V2.village_name
HAVING COUNT(*) = 1
);
This way you can use the engine's own functionality -- or at least that of its data providers -- to escape all characters (not merely double- and single quotes) as necessary.

But then, it should be like this (with one more doublequote each):
sSQL = "SELECT * FROM tblTranslation WHERE fldEnglish=""" & myString & """;"
Or what I prefer:
Make a function to escape single quotes, because "escaping" with "[]" would not allow these characters in your string...
Public Function fncSQLStr(varStr As Variant) As String
If IsNull(varStr) Then
fncSQLStr = ""
Else
fncSQLStr = Replace(Trim(varStr), "'", "''")
End If
End Function
I use this function for all my SQL-queries, like SELECT, INSERT and UPDATE (and in the WHERE clause as well...)
strSQL = "INSERT INTO tbl" &
" (fld1, fld2)" & _
" VALUES ('" & fncSQLStr(str1) & "', '" & fncSQLStr(Me.tfFld2.Value) & "');"
or
strSQL = "UPDATE tbl" & _
" SET fld1='" & fncSQLStr(str1) & "', fld2='" & fncSQLStr(Me.tfFld2.Value) & "'" & _
" WHERE fld3='" & fncSQLStr(str3) & "';"

I believe access can use Chr$(34) and happily have single quotes/apostrophes inside.
eg
DLookup("island", "villages", "village = " & chr$(34) & nonEscapedString & chr$(34))
Though then you'd have to escape the chr$(34) (")
You can use the Replace function.
Dim escapedString as String
escapedString = Replace(nonescapedString, "'", "''")

Parametrized queries such as Joel Coehoorn suggested are the way to go, instead of doing concatenation in query string. First - avoids certain security risks, second - I am reasonably certain it takes escaping into engine's own hands and you don't have to worry about that.

By the way, here's my EscapeQuotes function
Public Function EscapeQuotes(s As String) As String
If s = "" Then
EscapeQuotes = ""
ElseIf Left(s, 1) = "'" Then
EscapeQuotes = "''" & EscapeQuotes(Mid(s, 2))
Else
EscapeQuotes = Left(s, 1) & EscapeQuotes(Mid(s, 2))
End If
End Function

For who having trouble with single quotation and Replace function, this line may save your day ^o^
Replace(result, "'", "''", , , vbBinaryCompare)

put brackets around the criteria that might have an apostrophe in it.
SOmething like:
DLookup("island", "villages", "village = '[" & txtVillage & "]'")
They might need to be outside the single quotes or just around txtVillage like:
DLookup("island", "villages", "village = '" & [txtVillage] & "'")
But if you find the right combination, it will take care of the apostrophe.
Keith B

My solution is much simpler. Originally, I used this SQL expression to create an ADO recordset:
Dim sSQL as String
sSQL="SELECT * FROM tblTranslation WHERE fldEnglish='" & myString & "';"
When myString had an apostrophe in it, like Int'l Electrics, my program would halt. Using double quotes solved the problem.
sSQL="SELECT * FROM tblTranslation WHERE fldEnglish="" & myString & "";"

Related

Filter by multiple parameters on Access form

I have a MS Access form that I want to filter the database based on a SQL statement.
The form will use multiple parameters, but I want it so that not all fields are required to perform the filter.
An example would be: User wants to query only by Date and Product and leave Customer and Analysis blank.
These are the fields in the form:
So far I have tried the following statements and using "LIKE" but it is returning blank results. I have only tried with two fields and it isn't working.
Public Sub Command121_Click()
Dim task As String
task = "select * from SageOrderLines_Live where [PromisedDeliveryDate] = " & Format(Me.DateFrom, "\#dd\/mm\/yyyy\#") & " AND [CustomerAccountNumber] LIKE "" & Me.CustomerAccount & """
DoCmd.ApplyFilter task
End Sub
Using LIKE without wildcard might as well be = sign.
Use of quote delimiters is incorrect - really need another quote on each side.
" AND [CustomerAccountNumber] LIKE """ & Me.CustomerAccount & "*"""
Or make it easier to read and use apostrophe instead of doubled quotes.
" AND [CustomerAccountNumber] LIKE '" & Me.CustomerAccount & "*'"

Use a string (multivalued field from table) as where clause in SQL query

I try to redefine a SQL query with VBA to include the content of a multivalued table field as a where clause but I can't get it to work.
First I gather all the values from the specific multivalued field (formatted as text, can't use numbers due limitations during the import)
BLPN_Query = DLookup("Kostenstellen_Report", "Optionen_Reportgenerierung", "ID=1")
MsgBox will return this (mind the space between ";" and the next text):
34; 44
This string can contain several different text entries. I would like to use the string "BLPN_Query" as a where clause condition. What I've got so far is this:
"WHERE (PROKALK.NK_STK>0) AND (PROKALK.TERMIN>{d '" & Startjahr & "-01-01'}) AND (PROKALK.BLPNR='" & BLPN_Query & "')"
If there is only one entry it works, but in case there are more than one it won't work (obviously). Not sure but I guess the space between the semicolon and the next text is an issue as well as I have to use "IN" instead of "=" but I don't know how to do this.
Solution (thanks to Andre!)
1.) Get data from table (looks like: 34; 35; 36), remove the spaces and replace the semicolon by a comma including single quotes between the elements for the IN clause. Now it looks like: 34','35','36
BLPN_Query = DLookup("Kostenstellen_Report", "Optionen_Reportgenerierung", "ID=1")
BLPN_Query = Replace(BLPN_Query, " ", "")
BLPN_Query = Replace(BLPN_Query, ";", "','")
2.) Include the string within the where clause (and add a single quote before and after the string) --> Final string: '34','35','36'
AND (PROKALK.BLPNR IN ('" & BLPN_Query & "'))"
it's not pretty but will help us until we finally get the new ERP system and can replace all the old stuff
IN (...) expects comma, so you need to do:
BLPN_Query = Replace(BLPN_Query, ";", ",")
and then in the WHERE clause:
"WHERE ... AND (PROKALK.BLPNR IN (" & BLPN_Query & "))"
Extra spaces don't hurt. This also works if BLPN_Query contains a single value, but it doesn't if it is empty.

Inserting Single/double type to DB using VBA in access

I need some help with an issue that is doing my head in.
I need to update a database in access and its been working fine operating with Long and Integers.
Look at this code.
sql = "UPDATE DBNAME SET Long_Field = " & Long_Variable & " WHERE ID = " & id
DoCmd.SetWarnings (False)
DoCmd.RunSQL sql
This code runs flawlessly, it takes a long variable and puts it into the correct field which is set as Long.
However, if I want to populate a single/double field (ive tried both)
sql = "UPDATE DBNAME SET Double_Field = " & double_Variable & " WHERE ID= " & id
DoCmd.SetWarnings (False)
DoCmd.RunSQL sql
I keep getting Run-Time error 3144: Syntax error in update statement.
I can literally just switch out the field name and the variable name and the code runs flawlessly, but as soon as i try to send a double value, for example (5,8), to a field in the table that is set to double, it gives me this error.
Anyone?
I assume that you want a dot as decimal separator in your string.
The conversion from double to string is done using the separator from the system locale settings so in your case a comma.
This means that
double_variable = 5.8
sql = "... " & double_variable & " ..."
will produce ... 5,8 ... in the sql variable.
The easiest way to fix that is to use
"..." & Replace(CStr(double_variable), ",", ".") & "..."
This will replace all , with .. I put the CStr there to make sure it gets converted to a string first. It will also work if the system locale changes since nothing will happen if there is no ,. The only caveat is that if for some reason the conversion inserts 1000s separators it will fail but that would only be relevant in other circumstances as I don't think CStr will ever do that.
The current answer is not the easiest, neither the simplest.
The universal method is to use Str as it always returns a dot as the decimal separator:
sql = "UPDATE DBNAME SET Double_Field = " & Str(double_Variable) & " WHERE ID = " & id & ""

Get VBA to Evaluate Formula In String

I have a table that stores a string representing a formula that I would like to have either Access or VBA evaluate. A few example strings look like:
table.FirstName & ' ' & table.LastName
table.LastName & ', ' & table.FirstName
table.LastName & ', ' & table.FirstName & ' ' & LEFT(table.Middle,1)
Basically, I'm trying to change how different names can be viewed based on entity type, missing information, etc.
Is there any way to force either Access (in a query) or VBA (as part of a custom function) to return what the string is telling it as opposed to the literal value? From the examples above, I would expect:
table.FirstName table.LastName
table.LastName, table.FirstName
table.LastName, table.Firstname, t
Replace by itself won't work, as some of the formatting includes LEFT(table.Name,1) or other functions. I'm just hoping there is a simple way to force the string to be evaluated, rather than having to come up with a complex function.
I apologize if I haven't explained this well, I feel like my attempt to merge the database aspect with the string formatting aspect may not come across clearly. If you have questions please reply and I'll do my best to explain it better.
Thank you in advance.
How about the Microsoft Access Eval() function:
?Eval("3 + 1")
4
You could build an SQL string from your table formulas like:
SQL = "Select " & tblFormulas.FormulaField.Value & " From " & Split(tblFormulas.FormulaField.Value, ".")(0) & ";"
Not bullet-proof but a start ...
As you have a special table with formulas you can aim at queries and/or VBA.
For VBA you can store code for eval function to be run in form of several forms with same set of required fields(that is used in formulas):
in field VBAformula of your special table should resides such string me![LastName] & ', ' & me!FirstName & ' ' & LEFT(Me![Middle],1)
then in some function you could write:
formula = dlookup("VBAformula","SpecialTable","id=" & me![HowToViewID])
Me![ViewAs] = eval(formula)
For Queries you can store SQL formula to be used in UPDATE query, SQLformula with such string:
[LastName] & ", " & [FirstName] & " " & LEFT([Middle],1) then you can build and run SQL:
formula = dlookup("SQLformula","SpecialTable","id=" & me![HowToViewID])
currentdb.Execute "UPDATE [table with people] SET [ViewAs] = " & formula & " WHERE peopleID=" & Me![peopleID] & ";" , dbFailOnError
These are examples to show some variants, so there is no error handling or security.

VBA select not returning any rows

Below query is not returning any rows into the listbox.There is no error message:
lstDiff.RowSource = "select TestScenario,TestId from tblTesting where empid= '" & Me.txtEmpId.Value & "' and testid= '" & Me.txtAutoNumber.Value & "'"
Could anyone help?
Your values in the field are numeric, so the extra single quotes aren't needed. Code should look like the following:
Me.lstDiff.RowSource = "select TestScenario,TestId from tblTesting where empid= " & Me.txtEmpId & " and testid= " & Me.txtAutoNumber & ";"
I've also dropped .Value from the field references, they're not harmful, but also aren't necessary.
And I've added a semi-colon to the end of your statement.
Depending on when/where you insert this code, you may need to add the following statement as well:
Me.lstDiff.Requery
You keep posting questions about the exact same WHERE clause with exactly the same apparent error in each one. SO users dutifully point out your error and then a few days later, you show up with a related question utilizing the same faulty WHERE clause.
DLookup Problem:
txtContSunStart1.Value = DLookup("Start1", "tblContract", _
"TestId = " & _
lstResults.Value & _
"" And "Day = 'Sunday'")
VBA Update Query:
DoCmd.RunSQL (" Update tbltesting set IsDiff ='Yes' where empid= " & Me.txtEmpId.Value & " and testid= " & Me.txtAutoNumber.Value & ";")
VBA SQL Problem
DoCmd.RunSQL ("insert into tbltesting (IsDiff)values ('Yes') where empid= '" & Me.txtEmpId.Value & "' and testid= '" & Me.txtAutoNumber.Value & "'")
And then in the current question:
lstDiff.RowSource = "select TestScenario,TestId from tblTesting where empid= '" & Me.txtEmpId.Value & "' and testid= '" & Me.txtAutoNumber.Value & "'"
You are having difficulties with exactly the same set of problems repeatedly.
Here are the rules for concatenating SQL strings with the correct delimiters in Access:
numeric values do not need delimiters:
"... AND testid= " & Me!txtAutoNumber
text values need quote delimiters. In Access, it's general practice to use double quotes, but much easier to use single quotes since it's a pain to type double quotes in a form that will work (typing """ or """" depending on context is counterintuitive and silly to me, so I always define a global constant that holds the double quote symbol and concatenate with that).
"... AND textfield=" & Chr(34) & Me!MyTextField & Chr(34)
date values use the # delimiter:
"... AND datefield=#" & Me!MyDateField & "#"
Boolean fields require no quotes and it works best to use True and False:
"... AND IsDiff=True"
These rules apply both to WHERE clause criteria and to SET statements in UPDATE queries. The rules apply in writing a SQL string that you pass to DoCmd.RunSQL or CurrentDB.Execute, as well as to writing SQL strings to be used as the recordsource of a form or report or as the rowsource of a combo box or listbox.
Personally, whenever I use SQL statements in code, I prefer to store the statement in a variable. While testing, on the line after you assign your statement to a variable, you can use Debug.Print to see what your SQL statement looks like after parsing your txtempid and txtautonumber. It would look something like this.
Dim sSQL as String
sSQL = "select TestScenario,TestId from tblTesting where empid= '" & Me.txtEmpId.Value & "' and testid= '" & Me.txtAutoNumber.Value & "'"
Debug.Print sSQL
lstDiff.RowSource = sSQL
Then as long as your immediate window is visible (Ctrl-G), you can see what your SQL statement really is. If it looks right in the immediate window, copy and paste it into the query builder and run it there.
Try running the query in your SQL Management Studio. Do you get any row(s) back?
Edit: Just noticed the access-tag. Are you sure your table contains at least one post with supplied ids?
My Access is a bit rusty, but if all else fails try using a recordset to capture the data from the SQL and loop through it adding the values to the list box. Example Code