This is a work project but I'm having trouble getting my head around using "insert into" for an MS Access DB.
I've tried the following 3 scripts and all return different errors and of course I want my initial problem resolved but I would also appreciate knowing why they are all failing and what's different/best practice. These scripts are all modified ones I've found from searching around:
Script1:
Const adOpenStatic = 3
Const adLockOptimistic = 3
Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")
objConnection.Open _
"Provider = Microsoft.Jet.OLEDB.4.0; " & _
"Data Source = C:\Users\itsupport\Documents\test.mdb"
objRecordSet.Open _
"INSERT INTO Users" & _
"(Id, Name, " & _
"CertificateName, Password) " & _
"VALUES ('michaelr', 'Michael Raymond', " & _
"'NULL', '888')", _
objConnection, adOpenStatic, _
adLockOptimistic
Error 1:
Syntax error in INSERT INTO statement
Script 2:
Set objConn = CreateObject("ADODB.Connection")
Dim rsData
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='C:\Users\itsupport\Documents\test.mdb'"
objConn.Open strConnection
strInsertSQL = "INSERT INTO Users (iD,Name,CertificateName,Password) VALUES('" & _
michaelr & "','" & Michael Jenkins & "','" & - & "','" & 888 & "', objConnection, adOpenStatic, adLockOptimistic);"
Set rsData = objConn.Execute(strInsertSQL)
Error 2:
Expected end of statement
Script 3:
sql1="INSERT INTO Users VALUES ('michaelr','Michael Jenkins',NULL,'888')"
constring="Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=C:\Users\itsupport\Documents\test.mdb;
User Id=admin;Password=;"
set con=createobject("adodb.connection")
con.open constring
con.execute sql1
con.close
Error 3:
Unterminated string constant
Script1:
Defining the ad* constants is necessary in plain VBScript (there are no libraries to be referenced)
Non-trivial concatenated strings should be assigned to a variable and displayed for visual inspection
INSERT INTO Users(Id, Name, CertificateName, Password) VALUES ('michaelr', 'Michael Raymond', 'NULL', '888') quotes NULL and uses the reserved word "Password" (see here)
Script 2:
The concatenation 'works' if you double quote michaelr, Michael Jenkins, and - (whatever that means)
String concatenation in VBScript is a mess; unnecessary concatenation should be avoid (as in Script 3)
Script 3:
The syntax error is caused by the bad quoting and missing & _ in the definition of constring (you did it correctly in Script 1)
In general:
A lot of problems of building (and safely using) SQL statements simply vanish if you use parameterized queries
String concatenation in VBScript is a mess; using Arrays and Join, dedicated quote functions, and replacement of placeholders (no variable interpolation/expansion here) will result in better code
Related
I'm using Microsoft Access to develop a database app. An important feature the user would need is to automatically send an email update to all relevant stakeholders.
The problem is that I'm getting
Run-time error '3075' Syntax error in query expression.
Here it is below:
Set rs = db.OpenRecordset("SELECT StakeholderRegister.[StakeholderID], StakeholderRegister.[ProjectID], StakeholderRegister.[FirstName], StakeholderRegister.[LastName], StakeholderRegister.[EmailAddress] " & _
" FROM StakeholderRegister " & _
" WHERE (((StakeholderRegister.[ProjectID]=[Forms]![ChangeLog]![cboProjectID.Value])) ;")
Funny thing is that I created a query table on Access to create the relevant recordset and the turned on SQL view to copy the exact sql string that's above. That query works however it opens an Input Parameter box, whereas this code should be using the value typed into a forms text box as a matching criteria.
To use a variable as a parameter, do not include it within the quotes:
" WHERE StakeholderRegister.[ProjectID]=" & [Forms]![ChangeLog]![cboProjectID].[Value]
or just
" WHERE StakeholderRegister.ProjectID=" & Forms!ChangeLog!cboProjectID.Value
Note: You really only need the square brackets when there is something like a space in the name, which is not the best practice anyway.
I also took the liberty to remove the parentheses, as they are not needed in such a simple WHERE clause, and can cause more trouble than they are worth.
Try,
Dim strSQL As String
strSQL = "SELECT StakeholderRegister.[StakeholderID], StakeholderRegister.[ProjectID], StakeholderRegister.[FirstName], StakeholderRegister.[LastName], StakeholderRegister.[EmailAddress] " & _
" FROM StakeholderRegister " & _
" WHERE StakeholderRegister.[ProjectID]=" & [Forms]![ChangeLog]![cboProjectID].Value & " ;"
Set rs = Db.OpenRecordset(strSQL)
if [ProjectID] field type is text then
Dim strSQL As String
strSQL = "SELECT StakeholderRegister.[StakeholderID], StakeholderRegister.[ProjectID], StakeholderRegister.[FirstName], StakeholderRegister.[LastName], StakeholderRegister.[EmailAddress] " & _
" FROM StakeholderRegister " & _
" WHERE StakeholderRegister.[ProjectID]='" & [Forms]![ChangeLog]![cboProjectID].Value & "' ;"
Set rs = Db.OpenRecordset(strSQL)
I need to update a SharePoint table which has a field name with Spaces. The field name is called Tracking log
Unfortunately there are lot of reports already created that don't let me recreate this column with a different name, so I need to find a workaround to address this special name.
Lot of blogs point me to revise the internal name and check the name given when it was created, so I checked the List Setting menu for the whole URL path and see the following:
https://sales.acs.com/SMS/_layouts/15/FldEdit.aspx?List=%7B686F9AF9%2DFB64%2D4AD9%2DA268%2D19A057C432DA%7D&Field=Tracking%5Fx0020%5Flog
Below is the script I've put together which runs perfectly fine for any other field without spaces in the name, but not specifically for this one.
Dim sSQL As String
' Build the connection string
sConn = "Provider=Microsoft.ACE.OLEDB.12.0;WSS;RetrieveIds=Yes;" & "DATABASE=" & sSHAREPOINT_SITE & ";" & "LIST=" & sDEMAND_ROLE_GUID & ";Authentication=Default;"
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Open the connection.
With cn
.ConnectionString = sConn
.Open
End With
ID = "12345"
fieldName = "Tracking_x0020_log" 'Display view is "Tracking log", I've tried also with Tracking%5Fx0020%5Flog without luck
FieldValue = "Test"
'sSQL = "SELECT * FROM EMEA_SALES WHERE (APA_number='" & Format(ID, "#") & "');"
sSQL = "UPDATE EMEA_SALES SET " & fieldName & " = '" & FieldValue & "' WHERE (APA_number='" & Format(ID, "#") & "');"
rs.Open sSQL, cn, adOpenDynamic, adLockOptimistic
cn.Close
Set cn = Nothing
End Sub
The error I've got is:
No value given for one or more required parameters.
When I change the fieldname for any other without spaces is run smoothly.
Any clue will be appreciated. Thanks folks!
I got a problem with running SQL query with "declare" and "set" functions in VBA.
Sheets("Arkusz1").Select
connstring = _
"ODBC;DRIVER=SQL Server;SERVER=my_database_server;UID=user;PWD=password;APP=Microsoft Office 2010;WSID=some_id;DATABASE=mydatabase"
With ActiveSheet.QueryTables.Add(Connection:=connstring, Destination:=Worksheets("Arkusz1").Range("A1"), Sql:=Array( _
"declare #dzisiaj date" & Chr(13), _
"set #dzisiaj = getdate()" & Chr(13), _
"select #dzisiaj as dzisiaj"))
.BackgroundQuery = False
.Refresh
End With
In SQL Server 2012 that code works fine, but... when I embed it into it gives me a run-time error '1004'. Also VBA code works on other queries works well.
My full SQL query has about 90 lines with 2 variable declarations (one declaration is a value from another 30 line SQL query), so it's mandatory to include variable declarations :)
How to solve that problem?
I figured it out. The key is to use ADODB connection to import data via SQL Query. Also necessary is to check Microsoft Active X Data Objects 2.0 library in Tools->References in Visual Basic Editor (Shortcut: Alt+F11 in Excel).
So, there is an example of my VBA code:
Sub sql_query_import()
' Declarations
Dim Cn As ADODB.Connection
Dim Server_Name As String
Dim Database_Name As String
Dim User_ID As String
Dim Password As String
Dim SQLStr As String
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
' Server connection settings
Server_Name = "192.168.1.106\my_database" ' IP of server name
Database_Name = "mydatabase" ' Database name
User_ID = "myusername" ' User name
Password = "mypassword" ' User password
' SQL Query
SQLStr = "SET NOCOUNT ON " & Chr(13) ' it's mandatory if you don't want to get error 3704
SQLStr = SQLStr & "declare #dzisiaj date " & Chr(13)
SQLStr = SQLStr & "set #dzisiaj = getdate() " & Chr(13)
SQLStr = SQLStr & "select #dzisiaj as 'today'
' Connect to database
Set Cn = New ADODB.Connection
Cn.Open "Driver={SQL Server};Server=" & Server_Name & ";Database=" & Database_Name & _
";Uid=" & User_ID & ";Pwd=" & Password & ";"
' Start connection
rs.Open SQLStr, Cn, adOpenStatic
' Load data
With rs
For i = 1 To .Fields.Count
Worksheets(1).Cells(1, i) = .Fields(i - 1).Name ' Include column name if not - delete it
Next i
End With
Worksheets(1).Cells(2, 1).CopyFromRecordset rs ' Start loading data to Cell A2
rs.Close
Set rs = Nothing
Cn.Close
Set Cn = Nothing
End Sub
Using in SQL Query "SET NOCOUNT ON" is necessary if you don't want to get error 3704.
Also, using
SQLStr = "SET NOCOUNT ON " & Chr(13) ' it's mandatory if you don't want to get error 3704
SQLStr = SQLStr & "declare #dzisiaj date " & Chr(13)
is more efficient way to include multi-line SQL Queries :)
I'm still new to vb and vba and learning myself, but I know you can declare and write to variables in VB.net which can then feed into an embedded SQL script. I would think you can do the same thing in vba. Here's what I suggest.
Declare a vb string like SQL_Var_1
Insert the 30-line SQL query as a separate query before the main query.
Write the result of the 30-line query to the vb string SQL_Var_1.
Remove the declarations from the Main SQL query but leaving the references to those variables.
Reference SQL_Var_1 as an input parameter in the embedded main query using the exact same name you used in the main query (i.e., #dzisiaj), like here.
If you follow these steps for both SQL variables, you should be able to achieve the same result as if you were using the declared SQL variables.
I am using the code below to create a new record in the "transactions table" the second line of the insert statement is throwing an error: Too few parameters. I have double checked and all of the field names are correct. What else could cause this type of error?
' Modify this line to include the path to Northwind
' on your computer.
Set dbs = CurrentDb
Dim vblCustomerID As String
Dim vblMealType As String
Dim Charge As Currency
Dim vblDate As String
vblDate = Format(Date, "yyyy-mm-dd")
txtCustomerID.SetFocus
vblCustomerID = txtCustomerID.Text
txtMealType.SetFocus
vblMealType = txtMealType.Text
txtCharge.SetFocus
vblCharge = txtCharge.Text
dbs.Execute "INSERT INTO dbo_Transactions" _
& "(CustomerID, MealID, TransactionAmount, TransactionDate) VALUES " _
& "(" & vblCustomerID & ", " & vblMealType & ", " & vblCharge & ", " & vblDate & ");"
dbs.Close
As others have suggested, using a parameterized query is a much better way of doing what you're attempting to do. Try something like this:
Dim qdf As DAO.QueryDef
Set qdf = dbs.CreateQueryDef("", _
"PARAMETERS prmCustomerID Long, prmMealID Long, prmTransactionAmount Currency, prmTransactionDate DateTime;" & _
"INSERT INTO dbo_Transactions (CustomerID, MealID, TransactionAmount, TransactionDate) " & _
"VALUES ([prmCustomerID], [prmMealID], [prmTransactionAmount], [prmTransactionDate]) ")
qdf!prmCustomerID = txtCustomerID.Value
qdf!prmMealID = txtMealType.Value
qdf!prmTransactionAmount = txtCharge.Value
qdf!prmTransactionDate = Date()
qdf.Execute dbFailOnError
Set qdf = nothing
Do any of the text fields you're loading into your vbl fields contain special characters like these?
, ' "
All of those in a text field in a perfectly good SQL Insert command could screw things up, I bet that's what happening here.
It would be better if you actually use parameters here to, rather than loading the text in textboxes directly into your SQL queries, since you're opening yourself up to SQL Injections. What if someone types
"; Drop Table dbo_Transactions;
in one of your textboxes and you run this query? Your database is then totally screwed up because someone just deleted one of your tables.
A few links to info on using Parameters to prevent this issue, which I'll bet will also fix the too few parameters issue you're having.
http://forums.asp.net/t/886691.aspx
http://sqlmag.com/blog/t-sql-parameters-and-variables-basics-and-best-practices
I have a query which uses an array of tables (stored as a string array) to loop through my tables and perform a Delete/Update set of queries. The queries are identical other than the table names, hence using a loop to iterate through using the table name as a variable.
The problem is, my Delete query locks the table and the Update query runs too quickly afterward; I get a "db is locked" error.
I need either of two things:
A way to tell VBA to "wait for previous command" OR
A way to concatenate these queries into one (or two) queries: one to delete the database rows and another to import the new ones. With this I could just run the queries from a standard access query (which should allocate proper time, finish queries etc)
The only catch to this is that there are parent-child relations, so the parent table has to be updated before its children (currently accomplished through array ordering).
Here's the current code which (sometimes) produces the "DB locked/in use" message:
For i = 0 To UBound(tables)
'Delete all data first
sql = "DELETE * FROM " & tables(i)
DoCmd.RunSQL sql
'Update all data second
sql = "INSERT INTO " & tables(i) & " IN """ & toDB & """ SELECT " & tables(i) & " .* FROM " & tables(i) & " IN """ & fromDB & """;"
DoCmd.RunSQL sql
Next
Should clarify: the queries take one backend's (fromDB) rows from identical tables and pushes it to another backend's (toDB) rows
EDIT: In response to the questions regarding INSERT INTO, my problem with that is if I add fields to the toDB, it will delete them if I overwrite. The reason I have to do this backdoor approach is because the database is still in development, but is also being used with select tables. Updates and feature improvements are done daily. I cannot use a simple split-backend either, because the other computer accessing the database is not always on the network (we have to manually sync it when it returns to the network), so I work on one backend and it works on another, identical(ish, minus my schema update) backend.
You can use ADO instead of DoCmd.RunSQL to execute your SQL synchronously.
Dim cmd As ADODB.Command
Dim cnn As New ADODB.Connection
Set cnn = CurrentProject.Connection
For i = 0 To UBound(tables)
Set cmd = New ADODB.Command
With cmd
.CommandType = adCmdText
.ActiveConnection = cnn
.CommandText = "DELETE * FROM " & tables(i)
.Execute
End With
Set cmd = New ADODB.Command
With cmd
.CommandType = adCmdText
.ActiveConnection = cnn
.CommandText = "INSERT INTO " & tables(i) & " IN """ & toDB & """ SELECT " & tables(i) & " .* FROM " & tables(i) & " IN """ & fromDB & """;"
.Execute
End With
Next
You could also add cnn.BeginTrans and cnn.CommitTrans to make the two statements Atomic.
Try something like this (Note I've commented your DoCmd.RunSQL. I changed it with db.Execute:
Sub DeleteInsertData(tables() As String, toDB As String, fromDB As String)
Dim db As DAO.Database
Dim i As Integer
Dim SQL As String
Set db = CurrentDb()
For i = 0 To UBound(tables)
'Delete all data first
SQL = "DELETE * FROM " & tables(i)
db.Execute SQL, dbFailOnError
'DoCmd.RunSQL SQL
'Update all data second
SQL = "INSERT INTO " & tables(i) & " IN """ & toDB & """ SELECT " & tables(i) & " .* FROM " & tables(i) & " IN """ & fromDB & """;"
db.Execute SQL, dbFailOnError
'DoCmd.RunSQL SQL
Next
Set db = Nothing
End Sub
I'm no expert, but I don't think you need the DELETE part. SELECT INTO is the syntax for making a table. If the table already exists, it's overwritten.
If I need to wait for something, I tend to use DoEvents to allow windows to process any other actions that have been put on hold.
From the help: "Yields execution so that the operating system can process other events."
Found this:
.BeginTrans then
.CommitTrans dbForceOSFlush
as a way to force updates to be written before continuing