VBA Excel SQL INSERT query adds apostrophe to all data inserted - sql

I'm trying to use an INSERT statement to insert data from a userform to an excel spreadsheet, but it adds apostrophes to every inserted value, even dates and numbers.
How can I do an insert statement that will not insert any apostrophe for any value?
Code I'm using currently:
Sub Insert_data()
Dim con As Object
Dim vSQL1 As String
Dim dbpath As String
dbpath = myworkbookpath
Set con = CreateObject("ADODB.Connection")
con.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbpath & ";Extended Properties=""Excel 12.0;HDR=Yes;IMEX=0"";"
con.Open
'CancelledFeeEntryForm.LogDate.Value = Date
'CancelledFeeEntryForm.RecordAppNum.Value = 123456789
vSQL1 = "INSERT INTO [Sheet1$] ([Employee],[Date Of Call],[Application Number]) " & _
"VALUES('" & CancelledFeeEntryForm.Employee.Value & "'," & _
"#" & CancelledFeeEntryForm.LogDate.Value & "#," & _
CancelledFeeEntryForm.RecordAppNum.Value & ");"
con.Execute (vSQL1)
con.Close
End Sub

You should've debugged and looked at what exactly vSQL1 is containing.
From looking at it, this is what your SQL statement is going to look like:
... VALUES ('SomeStringValue',#SomeDateValue,123')
... aka, there's an apostrophe at the end of the numerical value... but not at the beginning.
To be honest, I'm glad Excel VBA is handling it like this. Because the alternative would be having an open security hole for SQL Injection Attack (I was about 5 seconds away from going on a rant about how you should never do SQL statements like this, until I noticed that VBA protected you from a serious security mistake.)

Figured out a work around, although it's annoying, it'll have to do for now.
As is with excel in many cases, I had to enter dummy data on line 2 of the workbook i'm inserting data in the format I want. Then, when using the SQL insert code, it will match the existing data.
If anyone knows how to do it through code, feel free to pitch in. thanks

Related

Inserting into empty Excel tables with SQL, without losing data type property

I'm running som VBA code with SQL to insert from one sheet to another. My problem is that when I use the INSERT INTO statement, I lose the data type property in empty tables.
This is the code I'm running:
Sub InsertTest()
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
Dim fromFile As String
fromFile = "C:\some\file.xlsx"
Dim cnStr As String
cnStr = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & fromFile & ";" & _
"Extended Properties=Excel 12.0"
Dim query As String
query = "INSERT INTO [TableTwo$](lastname, age) SELECT lastname, age FROM [TableOne$]"
rs.Open query, cnStr, adOpenUnspecified, adLockUnspecified
End Sub
Table one is where I get my data from:
Table two is where I insert data, here is how it looks after the code is executed:
I noticed that if TableTwo is populated with data of correct data types, the INSERT INTO statement also inserts data of correct data type. I don't know if it's excel who's doing this or if its ADODB. Do you know?
I've tried to alter the schema of the TableTwo. I've also tried the CAST() and CONVERT() functions, but I can't make it work:(
I know that I easily can fix data types by some simple VBA code, but in my mind that defeats some of the purpose of using SQL. I would really hate to iterate over my sheets and tables with VBA because it's incredibly much slower than SQL in my experience.
Another thing I noticed is that when I use SQL, the data in the formula line has a quotation mark (like this ') in the beginning of the word. But it doesn't in the cell. Why does it do that?
Thanks in advance!

SQL statement in VBA

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.

vbScript - InputBox into .accdb , blank?

After 9 hours of research and trial and error, I've come to all of you with this current issue I am having with another script I wrote to input data into a database. I'll simplify the focal point of this problem in the code view. I have 3 fields, one is a timestampe at the end of the SQL such as .... & now() & "')". That inserts FIND, but by variable inputboxes insert blank data.
Option Explicit
Dim ib
Dim sql1, constring, con
constring="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\users\user\documents\database1.accdb;"
sql1="INSERT INTO table1 (column1) VALUES('" & ib & "')"
set con = createobject("adobd.connection")
con.open constring
Do
ib=inputbox("Input Data")
IF ib="quit" OR ib="QUIT" THEN
con.close
ELSE con.execute sql1
End If
Loop WHILE ib<>"quit" AND ib<>"QUIT"
It is appending rows into the database, but they are blank. Ive designed the table to take short text of <255 chars.
I also ran a test query from within the database and it inserts what I tell it to, but the inputbox data, for some reason, is not making it to the table.
This
sql1="INSERT INTO table1 (column1) VALUES('" & ib & "')"
concats an (empty) ib into sql1 before the loop. con.execute sql1 will insert that into the database until you quit.
Try
con.execute "INSERT INTO table1 (column1) VALUES('" & ib & "')"
and optimize when it works.
Do
ib=inputbox("Input Data")
If LCase(ib) <> "quit" THEN
con.execute "INSERT INTO table1 (column1) VALUES('" & ib & "')"
End If
Loop WHILE ib<>"quit" AND ib<>"QUIT"
con.close
You need to recreate the string that you will execute for each value that you retrieve

Updating a field dependent on a date range in Access with VisualBasic and SQL

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.

SQL Statement "LIKE" operator doesn't find the cells where only one number exists

I'm reading from an Excel XLS worksheet with no header rows. Some cells in the column have a list of numbers like 12345, 12346, 12347, 12348. Other cells only have one number 12345.
The "LIKE" operator finds the number when there are multiple numbers in a cell, but doesn't find the cells where only one number exists.
SQL = "SELECT * FROM [2010 VIP$] WHERE F9 LIKE '%" & sDealer & "%'"
I tried changing my connection string from:
"Data Source=" & dS & ";Extended Properties=""Excel 8.0;HDR=No"""
To (adding IMEX for mixed data types):
"Data Source=" & dS & ";Extended Properties=""Excel 8.0;HDR=No;IMEX=1"""
But I get an unknown error when IMEX is added. It's my understanding you can't use the F1, F2, F3 field names without HDR=No.
I tried using the first connection string but changed my SQL to:
SQL = "SELECT * FROM [2010 VIP$] WHERE F9 LIKE '%" & sDealer & "%' OR F9='" & sDealer & "'"
But it still doesn't find the cells with only one number.
EDIT: I ended up using a slower method but it works and still checks 1200 rows in like 2 seconds:
Dim cN As New ADODB.Connection
Dim rS As New ADODB.Recordset
Dim SQL As String
Dim dDealer As Double
Dim WS As Worksheet
Dim sDealer As String, sAmount As String
Dim bFound As Boolean
Set WS = ActiveSheet
cN.Provider = "Microsoft.Jet.OLEDB.4.0"
cN.Open "Data Source=" & MostRecentPath & ";" & _
"Extended Properties=""Excel 8.0;IMEX=1"""
For dDealer = 2 To WS.Range("a60000").End(xlUp).Row
sAmount = WS.Range("c" & dDealer).Value
If Len(sAmount) > 0 Then GoTo skipOne
sDealer = Trim(WS.Range("i" & dDealer).Value)
If Len(sDealer) <> 5 Then GoTo skipOne
If IsNumeric(sDealer) = False Then GoTo skipOne
SQL = "SELECT * FROM [2010 VIP$]"
rS.Open SQL, cN, adOpenStatic, adLockOptimistic
bFound = False
Do While rS.EOF = False
If InStr(1, rS.Fields(8).Value, sDealer) > 0 Then
bFound = True
Exit Do
End If
rS.MoveNext
Loop
rS.Close
If bFound = True Then WS.Range("l" & dDealer).Value = "VIP"
DoEvents
skipOne:
Next dDealer
cN.Close
The single-number cells are numeric and won't be found using the string-based like operator. You could try forcing the cells to be text cells by prepending the number with an apostrophe (eg. '12345).
You could also try
SELECT * FROM [2010 VIP$] WHERE F9 LIKE '%" & sDealer & "%' OR F9=" & sDealer & "
(no single quotes in the second part of the where clause)
As well as setting the IMEX option, sort the data in the worksheet to ensure that the first eight rows contain cells that will be interpreted as text - i.e. ones with multiple values like 12345,12346,12347,12348
Quoting from this KB article:
NOTE: Setting IMEX=1 tells the driver
to use Import mode. In this state, the
registry setting ImportMixedTypes=Text
will be noticed. This forces mixed
data to be converted to text. For this
to work reliably, you may also have to
modify the registry setting,
TypeGuessRows=8. The ISAM driver by
default looks at the first eight rows
and from that sampling determines the
datatype. If this eight row sampling
is all numeric, then setting IMEX=1
will not convert the default datatype
to Text; it will remain numeric.
Alternatively, consider normalising your data in the spreadsheet as this is the real problem here
First, I agree with #barrowc: the underlying problem is that your 'lists of numbers' violates first normal form (1NF) and SQL is not designed to query non-scalar data types (i.e. lacks operators to exploit multi-valued data).
Second, you need to get your connection string and registry settings correct for ADO to 'see' the column as text. This article may help with this.
If you must work with the not first normal form (NFNF) data, you will need to handle the comma delimiters.
Here is some standard SQL with test data to demonstrate the point:
WITH Dealers (dealer_ID, delear_list)
AS
(
SELECT dealer_ID, delear_list
FROM (
VALUES (1, '12345,12346,12347,12348'),
(2, '12344,12345,12346'),
(3, '12343,12344,12345'),
(4, '12345'),
(5, '12399,12346,12347,12348'),
(6, '12344,12399,12346'),
(7, '12343,12344,12399'),
(8, '12399')
) AS Dealers (dealer_ID, delear_list)
)
SELECT dealer_ID, delear_list
FROM Dealers
WHERE (',' + delear_list + ',') LIKE ('%,12345,%');
Obviously, you'd need to port this to ACE/Jet dialect code e.g.
WHERE (',' & delear_list & ',') ALIKE ('%,12345,%');
Jet does not use % for pattern matching. It uses * instead. It also does match patterns against numbers, as if they were strings. So I suspect you would be able to return to your pattern matching approach if you changed your SQL string from "SELECT * FROM [2010 VIP$] WHERE F9 LIKE '%" & sDealer & "%'" to "SELECT * FROM [2010 VIP$] WHERE F9 LIKE '*" & sDealer & "*'".