Errors appending to a table in Access VBA - sql

I have an Access database with a VBA module that assembles a JSON string in a variable: JsonStr2 that I want to place in a simple table: JSONforAPI with two fields:
ID = Auto number and
JSON = Long String.
The JSON is valid and Jsonstr2 prints in the immediate window on Debug.Print.
When I run the code line:
DoCmd.RunSQL "INSERT INTO JSONforAPI (JSON) VALUES (" & Jsonstr2 & ")"
I get:
Run time error ‘3075’ Malformed GUID in query expression ‘{"order":{"CustomerID":"19"’.”
On clicking Help I get:
This command isn’t available. Your organization’s administrator turned off the service required to use this feature.
I get the same even if I create a Query Def.
I would be very grateful if someone could tell me what is wrong.

Since you are concatenating your value with the remainder of your SQL statement, none of the delimiters or other special characters within the string held by Jsonstr2 will be escaped and therefore you'll end up with all kinds of malformed strings within the SQL statement.
You should instead use parameters to avoid the need to escape the string, e.g.:
With CurrentDb.CreateQueryDef("","insert into jsonforapi (json) values (#json)")
.Parameters("#json") = jsonstr2
.Execute
End With

You could try this:
DoCmd.RunSQL "INSERT INTO JSONforAPI (JSON) VALUES ('" & Jsonstr2 & "')"
This will single quote Jsonstr2.
As you did not quote the string, it is passed as GUID, not a simple string.

Related

Query for text in a shortext field returns "Type Mismatch"

I am trying to populate ADODB Recordset, from a user input in a MS-Access form.
The data type the field in Access table shortext.
The data I am trying to get from the form input is also a text.
queryString="select * From tableshoporder where shopordernumber=" & me.txtInputShoporder.Value
adodb.recordset.open queryString,currentdb.connection,adopendynamic,adlockoptimistic
The above query throws "type mismatch error".
Since you state
The data type of the field in the Access table is short text. The data I am trying to get from the form input is also text.
You'll need to supply the value surrounded by string delimiters, e.g. either using single quotes:
queryString="select * From tableshoporder where shopordernumber='" & me.txtInputShoporder.Value & "'"
Or double quotes:
queryString="select * From tableshoporder where shopordernumber=""" & me.txtInputShoporder.Value & """"
However, note that this will break if me.txtInputShoporder.Value itself contains string delimiters. To avoid this, you can either use Gustav's cSQL function, or use parameters.

SQLite Inserts - How to Insert Any Text while Avoiding Syntax Errors using Parameterized Values

I'm curious to know a proper way to insert text strings into a database regardless of what characters are contained within the string. What I'm getting at is if I have a string for example that contains single quotes or any other character that is reserved as a 'special' SQL character.
The issue specifically that I'm dealing with is that I don't have control over the possible 'Text' that is being inserted into my database as they text is generated by my application.
One example of where my application fails to insert properly is when there's an error message that happens to contain single quotes. Single quotes are used by SQL statements insert text of course and so I can't insert text that also contains single quotes (outside of using ascii value char(##) function). The issue I'm dealing with is that I'm setting the output of my application to a string variable to then be inserted into my database so I can log activity whether that be standard output or catching errors.
How do I simply INSERT what's contained in my string variable while avoiding all of the SQL Special characters?
Do I need to manually account for all of the SQL Special characters and replace them in my string prior to insert? This sounds like a hit or miss and I'm hoping that there's something already built to accommodate this situation.
Sample Pseudo Code to get the point across:
Let's say an error occurred within the app and it needs to be logged. The error string could be:
Error String: "Error in function calculateStats() parameter 'pBoolStatCheck' is Null"
Now I assign to my string variable within my app and build up the SQL Insert string.
var insertString = "Error in function calculateStats() parameter 'pBoolStatCheck' is Null"
INSERT INTO outputLog(outputLogText)
VALUES ('insertString'); --This will Fail
--Fails due to the variable 'insertString' containing single quotes.
INSERT INTO outputLog(outputLogText)
VALUES ('Error in function calculateStats() parameter 'pBoolStatCheck' is Null');
In closing - since I have no control over the text that could be created by my application how do I account for all of the possible characters that could break my insert?
The code my current application encountering this issue is written in Visual Basic. The database I'm working with is SQLite.
The final solution based on answers received by this post:
Public Sub saveOutputLog(pTextOutput, pTextColor, pNumNewLines, plogType, pMethodSub)
Dim strTableName As String = "outputLog"
Dim sqlInsert As String = "INSERT INTO " & strTableName & "(" &
"outputLog, logColor, numNewLines, logType, method" &
") VALUES (#outputLog, #logColor, #numNewLines, #logType, #method);"
Try
'Open the Database
outputLogDBConn.Open()
'Create/Use a command object via the connection object to insert data into the outputLog table
Using cmd = outputLogDBConn.CreateCommand()
cmd.CommandType = CommandType.Text
cmd.CommandText = sqlInsert
cmd.Parameters.AddWithValue("#outputLog", pTextOutput)
cmd.Parameters.AddWithValue("#logColor", pTextColor)
cmd.Parameters.AddWithValue("#numNewLines", pNumNewLines)
cmd.Parameters.AddWithValue("#logType", plogType)
cmd.Parameters.AddWithValue("#method", pMethodSub)
'Execute the command using above Insert and added Parameters.
cmd.ExecuteNonQuery()
End Using 'Using outputLogDBComm
outputLogDBConn.Close()
Catch ex As Exception
outputLogDBConn.Close()
End Try
End Sub
This problem is very similar to the problem of preventing SQL Injection vulnerabilities in your code. Queries are very easy to break, and while yours breaks in a way that is harmless and annoying, these can be taken to levels where certain inputs can completely destroy your database!
One of the best ways to approach this is by using parameterized queries. This approach is pretty simple; you write the query first with 'placeholders' for the parameters you will send. Once you are ready in your program you can then 'bind' those parameters to the placeholders.
It would look something like the following:
Dim command = New SqlCommand("INSERT INTO outputLog(outputLogText) VALUES (#stringToInsert)", connection);
.
.
.
code
.
.
.
command.Parameters.AddWithValue("#stringToInsert", errorMessage)
.
.
.
execute query
In the above you see that #stringToInsert is the placeholder which is only bound at a later time. It doesn't matter what the variable errorMessage contains, since it will not cause the query to function in a way where the input causes it to potentially break.
There are a lot of resources on this:
https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.prepare?view=netframework-4.7.2
Database objects like connections not only need to be closed but also disposed. A Using block will ensure this will happen even if there is an error.
The .Add is better than .AddWithValue for a number of reasons. See https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/ and http://www.dbdelta.com/addwithvalue-is-evil/
The parameters of .Add are the name of the parameter, the datatype in the database and optionally the size of the field. You will need to check the database for the last 2.
Open the connection at the last minute befor the execute.
If you are using OleDb with Access, you need to make sure the parameters are added in the same order as they appear in the sql statement.
Using cn As New SqlConnection("Your connection string")
Using cmd As New SqlCommand("INSERT INTO outputLog(outputLogText) VALUES (#outputLogText);", cn)
cmd.Parameters.Add("#outputLogText", SqlDbType.VarChar, 100).Value = TextBox1.Text
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Using

How to build proper Access SQL LIKE operator expression?

I'm attempting to have a user search through a table in Microsoft Access 2010, but the SQL command isn't working. The command that loads and refreshes the table is this:
SELECT Equipment.equipmentID, Equipment.equipmentName, Equipment.model,
Equipment.make, Equipment.equipmentLocation FROM Equipment ORDER BY Equipment.equipmentName;
This works, but when I try to use a variable (or any normal criteria):
searchItem = Me.searchBox.Value
Me.List64.RowSource = "SELECT Equipment.equipmentID, Equipment.equipmentName,
Equipment.model, Equipment.make, Equipment.equipmentLocation FROM Equipment
WHERE Equipment.equipmentName LIKE '%searchItem%' ORDER BY Equipment.equipmentName;"
I've also tried something like "%10%" instead of the searchItem variable, but the command has the table come up blank with no errors. I suspect the problem is with the Equipment.eqiupmentName as the column name, but I can't quite figure out what's wrong here.
Here's a quick look at what the table looks like:
Try this:
Me.List64.RowSource = & _
"SELECT Equipment.equipmentID, Equipment.equipmentName," & _
" Equipment.model, Equipment.make, Equipment.equipmentLocation FROM Equipment" & _
" WHERE Equipment.equipmentName LIKE '*" & searchItem & "*'" & _
" ORDER BY Equipment.equipmentName;"
User rjt011000 has a valid solution, but I recommend using & for string concatenation in VBA (and Access). For an explanation of + and & see this thread.
Access will not recognize or substitute VBA variables inside an SQL statement. Furthermore, the LIKE operator is fed an SQL string value in this case (inside single quotes... which are inside the double quotes), so even if a VBA variable could be referenced directly inside SQL, Access does not interpret any such thing inside a string value.
Regarding the Access SQL LIKE operator, the multi-character matching pattern is * rather than %. Access also recognizes the operator ALIKE which does indeed honor the ANSI pattern %. See LIKE operator docs and this thread regarding ALIKE.
To be more thorough, the string delimiters and LIKE pattern-matching character should be escaped if you don't want the user inadvertently injecting invalid characters that cause errors in the SQL. Following is an example of escaping a couple of them. There are more elegant ways to handle this for all special characters, but the code and technique are beyond the scope of this answer.
...'" & Replace(Replace(searchItem, "*", "[*]"), "'", "''") & "'...
For the record, although Access SQL will not substitute a VBA variable, it will recognize and call a public VBA function. Normally such a public function must be defined in a normal module, but in context of a form's Record Source query, a form-module method can sometimes be called.
One last technique... It is possible to reference a form control's value directly in SQL. This can be very convenient and reduce extra code, but there are a couple caveats:
The form must of course be open, otherwise Access will interpret the reference as an unknown parameter and display a prompt. This will of course not be a problem if the SQL is always in context of the same form.
Access will sometimes automatically refresh the query when such a referenced control is changed, but it is not always guaranteed. The "timing" of automatic refreshes might not be immediately intuitive. You can call the Refresh method on the control or subform from various form events to force the query to refresh after the value is changed.
Notice that in the following example, the string concatenation is inside the VBA string, so that the concatenation actually happens in context of SQL and not beforehand like in the first code snippet. There is no problem with this, just something to consider since this entire answer revolves around proper string interpretation and concatenation.
But really, the same concern exists for un-escaped pattern-matching characters in the user text. Rather than making the SQL text long and ugly with calls to Replace(), instead create a custom function (e.g. EscapePattern()) that does this for any text and then wrap the control reference with that function. The example does this, although I don't include the code for the special function. Such a function could also be used in the first VBA code snippet to simplify building the SQL text.
Me.List64.RowSource = & _
"SELECT Equipment.equipmentID, Equipment.equipmentName," & _
" Equipment.model, Equipment.make, Equipment.equipmentLocation FROM Equipment" & _
" WHERE Equipment.equipmentName LIKE ('*' & EscapePattern(Forms![Form Name]![Control Name]) & '*')" & _
" ORDER BY Equipment.equipmentName;"
There is always more! Did you see the VBA line continuation in my example? It makes the SQL text much easier to view within VBA editor.
I suspect you are not setting your searchItem variable correctly in the SQL string. I am not too familiar with access string concatenation but try separate the searchItem out of the SQL string and then checking if your RowSource has the value you suspect.
Me.List64.RowSource = "SELECT Equipment.equipmentID, Equipment.equipmentName,
Equipment.model, Equipment.make, Equipment.equipmentLocation FROM Equipment
WHERE Equipment.equipmentName LIKE '%" + searchItem + "%' ORDER BY Equipment.equipmentName;"

Access VBA - Pass Param to SQL query?

I have created a function in VBA to connect to a web service and retrieve an XML file.
I then wanted to parse the XML and import the captured nodes into the SQL database.
I have managed to connect to the web service and parse the XML file and even show the values in a messagebox, but when i try and store the parameter via a SQL query, it asks me for a parameter value?
Example:
Set BodyStyle = domResponse.SelectSingleNode("/GetVehicles/DataArea/Vehicles/Vehicle/BodyStyle")
MsgBox (BodyStyle.Text)
DoCmd.RunSQL "INSERT INTO vHPI (BodyStyle) BodyStyle.Text"
The messagebox pops up with a value of MOTORCYCLE, but then i get a prompt asking me for a parameter for BodyStyle.Text
I dont understand how the system can show the parameter in a messagebox but say the parameter is empty when i want to insert it into the database?
Please help!
Thanks
Adam.
You can simply do this:
DoCmd.RunSQL "INSERT INTO vHPI (BodyStyle) VALUES ('" & BodyStyle.Text & "')"
Assuming your column is named BodyStyle and the table is vHPI
Right now you have the literal value of "BodyStyle.Text" on your SQL statement. Try it like this...
DoCmd.RunSQL "INSERT INTO vHPI (BodyStyle) " + BodyStyle.Text

Using a Dlookup in Access VBA when the field has a single quote

I've found a lot on this topic but still can't seem to get it to work. I have the following line of code:
If isNull(DLookup("id", my_table, my_field & "='" & temp_value & "'")) Then
The problem is a value in my_field of my_table is "O'Connell" (with a single quote), and I'm not sure how to get Dlookup to find it. I've tried using:
my_field & "=" & chr(34) & temp_value & chr(34)
And a host of other multi-quote options, but I just can't seem to get it to work. Though I can use VBA to modify the temp_value to include or not include the single quote, since the single quote already exists in the table, I need to make sure it matches. I'm just not sure how to tackle it.
Though the suggestions and answers here do work and resolve many issues with quotes in text, my issue ended up being related to the character I was seeing as a single quote not really being a single quote. For what it's worth, the data I was using was exported from Siebel, and the single quote I was seeing was actually chr(146), where a regular single quote (I say "regular" for lack of a better term) is chr(39).
If having issues with quotes, I found it helpful to examine the chr values of each character in the string. There may be a better way to do this, but this loop should help:
for i=1 to len(a_string)
debug.print mid(a_string,i,1) & " - " & asc(mid(a_string,i,1)
next i
The asc function gives you the chr code for a character, so this loops through the string and shows you each character and its associated chr code in the Immediate window (using debug.print). This also helps in finding other "hidden" (or non-visible) characters that may exist in a string.
Once discovered, I used the replace function to replace chr(146) with two single quotes (two chr(39)s), as suggested by HansUp, and that worked perfectly.
In this example, my_table is the name of my table and my_field is the name of a field in that table.
Dim strCriteria As String
Dim temp_value As String
temp_value = "O'Connell"
' use double instead of single quotes to avoid a '
' problem due to the single quote in the name '
strCriteria = "my_field = """ & temp_value & """"
Debug.Print strCriteria
If IsNull(DLookup("id", "my_table", strCriteria)) Then
MsgBox "no id found"
Else
MsgBox "id found"
End If
If you prefer, you can double up the single quotes within the name. This should work, but make sure you can distinguish between which are double and which are single quotes.
strCriteria = "my_field='" & Replace(temp_value, "'", "''") & "'"