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.
Related
I have 100 linked tables in ms-Access with the Name "TBL*"
and they have the same columns. I tried to create a module using vba that deletes some rows according to an sql query as follows:
Option Compare Database
Option Explicit
Sub DeleteRecords()
Dim strSQL As String 'sql statement
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Set db = CurrentDb
For Each tdf In db.TableDefs
DoCmd.SetWarnings False
If Not (tdf.Name Like "MSys*") And tdf.Name Like "TBL*" Then
db.Execute "DELETE FROM " & tdf.Name & " WHERE " & tdf.Fields(2) & " NOT LIKE '%MUST NOT DEL%'", dbFailOnError
End If
'DoCmd.RunSQL strSQL
DoCmd.SetWarnings True
Next
End Sub
Which means I need only the rows that their second column is like '%MUST NOT DEL%' and delete the others. This code gives me an error of invalid operation. I tried a lot of changes but nothing. I think that maybe I have a syntax error on my query. Any ideas what's wrong?
The "Invalid operation" error happens because the code references just tdf.Fields(2) where you want to include the field name in your SQL statement. That error should go away if you explicitly ask for the field's name: tdf.Fields(2).Name
However, you mentioned the "second column" is the one which may contain text matching '%MUST NOT DEL%'. And, since the Fields collection is zero-based, you need Fields(1).Name instead of Fields(2).Name
There is another potential problem lurking. When executing a query from CurrentDb, Access expects * instead of % as the wild card unless you have set the Access option for "SQL Server Compatible Syntax (ANSI 92)". Since I don't know which case applies to you, I used ALIKE instead of LIKE ... which signals the db engine to expect the ANSI 92 % wildcard.
Test the WHERE clause with a SELECT query to make sure it targets only the rows you later want to delete.
For Each tdf In db.TableDefs
If tdf.Name Like "TBL*" Then
strSQL = "DELETE FROM [" & tdf.Name & "] WHERE [" & tdf.Fields(1).Name & "] NOT ALIKE '%MUST NOT DEL%'"
Debug.Print strSQL ' you can inspect the completed statement in the Immediate window;
' Ctrl + g will take you there
db.Execute strSQL, dbFailOnError
End If
Next
Notes:
Since you're not using DoCmd.RunSQL to run your query, I don't think you need DoCmd.SetWarnings False here.
Whenever tdf.Name Like "TBL*" is True, Not (tdf.Name Like "MSys*") must also be True. So you don't need both for your If ... Then condition; Like "TBL*" is sufficient.
I bracketed the table and field names to avoid problems if either of those names include spaces, punctuation, or match the names of functions or keywords.
I'm using MS Access 2013. My current issue is with the following code, I use to log user activity.
Table is called: tbl-activitylog and has five columns :
id
timestamps
Username
Activity
Additional
I checked code many times char after char and don't know what's wrong :(
TempVars("UserName").Value = "admin"
Logging("Logon", "system")
Public Sub Logging(Activity, Additional As String)
Dim sql_code As String
sql_code = "INSERT INTO tbl-activitylog(Username, Activity, Additional) VALUES('" & TempVars("UserName").Value & "','" & Activity & "','" & Additional & "')"
Debug.Print sql_code
CurrentDb.Execute sql_code
End Sub
Debug print shows:
INSERT INTO tbl-activitylog(Username, Activity, Additional) VALUES('admin','Logon','System')
Becaus of using "-" you have to do it in this way [tbl-activitylog]
sql_code = "INSERT INTO [tbl-activitylog](Username, Activity, Additional) VALUES('" & TempVars("UserName").Value & "','" & Activity & "','" & Additional & "')"
This 3134 error denotes a syntax error in your INSERT statement. As the name of your table contains a dash, you need to enclose it between brackets :
INSERT INTO [tbl-activitylog]
(Username, Activity, Additional)
VALUES('admin','Logon','System')
Generally speaking you may as well enclose all fields and table names, to avoid all risks of clashes with ms-access reserved words, like :
INSERT INTO [tbl-activitylog]
([Username], [Activity], [Additional])
VALUES('admin','Logon','System')
Consider a parameterized query, an industry best practice in any application layer language running SQL in any database. With QueryDefs, you can parameterize queries in MS Access.
Even more MS Access will not allow you to save queries with syntax issues. So, be sure to escape special characters and reserved words with square brackets or backticks.
SQL (save below as a query object)
PARAMETERS UsernameParam Text, ActivityParam Text, AdditionalParam Text;
INSERT INTO [tbl-activitylog] ([Username], [Activity], [Additional])
VALUES ([UsernameParam], [ActivityParam], [AdditionalParam])
VBA (reference above query and bind values without quotes or concatenation)
TempVars("UserName").Value = "admin"
Logging("Logon", "system")
Public Sub Logging(Activity, Additional As String)
Dim sql_code As String
Dim qdef As QueryDef
Set qdef = CurrentDb.QueryDefs("mySavedQuery")
' BIND PARAMS
qdef![UsernameParam] = TempVars("UserName")
qdef![ActivityParam] = Activity
qdef![AdditionalParam] = Additional
qdef.Execute dbFailOnError
Set qdef = Nothing
End Sub
This query keeps telling me I'm missing a semicolon at end of SQL statement, but when I add it, it tells me that there's a character found at the end of the SQL statement, what is wrong here? I'm used to working with SQL Server so this is just plain confusing to me. I'm not sure why Access needs to use a ";" to close the query. I'm just not sure where to include it.
strSQL = "Insert Into [tempCaseMgmt]Values([planID],[EdId],[OrID],[TheDate], [TypeID],[UserID],[TimeStart],[TimeEnd],[Unitsled],[Unitsid],[ClientID], [GenderID])" _
& " Select * from [dbo_tempDetail] where [userid]= " & [Forms]![frmx]! [txtClientID] & ";"
I'm just trying to make this work. The values I'm selecting and inserting are identical.
As suggested by #Martin in one of the comments to his answer, you are mixing up the two forms of INSERT INTO, specifically,
INSERT INTO ... VALUES ...
and
INSERT INTO ... SELECT ...
In my own simplified example this fails with "Run-time error '3137': Missing semicolon (;) at end of SQL statement."
Dim strSQL As String
strSQL = "Insert Into [tempCaseMgmt]Values([planID],[UserID])" _
& " Select * from [dbo_tempDetail] where [userid]=1" & ";"
Dim cdb As DAO.Database
Set cdb = CurrentDb
cdb.Execute strSQL, dbFailOnError
whereas this works
Dim strSQL As String
strSQL = "Insert Into [tempCaseMgmt] ([planID],[UserID])" _
& " Select * from [dbo_tempDetail] where [userid]=1" & ";"
Dim cdb As DAO.Database
Set cdb = CurrentDb
cdb.Execute strSQL, dbFailOnError
Note that the Values keyword has been omitted.
Without looking too deep into it, I would say, you are missing a white space directly in front of your select Statement.
Update:
You missed a second white space in front of the "Values" keyword. Did you copy pasted this query, or did you just wrote it in?
I would say, that you try to use a mixed up statement syntax for the Insert Into Statement. Values is used for single record appending. That means you should have an semicolon after the closing parenthesis. For the interpreter the Select is a completely new Statement. I goes that is not what you want.
Use the multi record syntax for insert into:
"Insert Into [tempCaseMgmt] \n
Select * from [dbo_tempDetail] where [userid]= " & [Forms]![frmx]![txtClientID] & ";"
In this case column naming should be identically
best regards
Martin
I have a form in which one of the ComboBoxes lists all the documents of a given project. The user should select one and after pressing a button, and if present in Table Dessinsit opens a second form showing that record. If it is not present in that table, I want to add it in.
One of my collegues told me all I had to do was to execute an SQL query with VBA. What I have so far is this:
Dim rsDessin As DAO.Recordset
Dim strContrat As String
Dim strProjet As String
Dim strDessin As String
Dim sqlquery As String
'I think these next 3 lines are unimportant. I set a first query to get information I need from another table
strDessin = Me.Combo_Dessin
strProjet = Me.Combo_Projet
sqlquery = "SELECT [Projet HNA] FROM [Projets] WHERE [Projet AHNS] = '" & strProjet & "'"
Set rsDessin = CurrentDb.OpenRecordset(sqlquery)
If Not rsDessin.RecordCount > 0 Then 'If not present I want to add it
strContrat = rsDessin![Projet HNA]
sqlquery = "INSERT INTO Feuilles ([AHNS], [Contrat], [No Projet]) VALUES (strDessin, strContrat, strDessin)"
'Not sure what to do with this query or how to make sure it worked.
End If
'Checking my variables
Debug.Print strProjet
Debug.Print strContrat
Debug.Print strDessin
'By here I'd like to have inserted my new record.
rsDessin.Close
Set rsDessin = Nothing
I also read online that i could achieve a similar result with something like this:
Set R = CurrentDb.OpenRecordset("SELECT * FROM [Dessins]")
R.AddNew
R![Contrat] = strContrat
R![Projet] = strProjet
R![AHNS] = strDessin
R.Update
R.Close
Set R = Nothing
DoCmd.Close
Is one way better than the other? In the case where my INSERT INTO query is better, what should I do to execute it?
You're asking which is preferable when inserting a record: to use an SQL statement issued to the Database object, or to use the methods of the Recordset object.
For a single record, it doesn't matter. However, you could issue the INSERT statement like this:
CurrentDb.Execute "INSERT INTO Feuilles ([AHNS], [Contrat], [No Projet]) VALUES (" & strDessin & ", " & strContrat & ", " & strDessin & ")", dbFailOnError
(You should use the dbFailOnError option to catch certain errors, as HansUp points out in this answer.)
For inserting multiple records from another table or query, it is generally faster and more efficient to issue an SQL statement like this:
Dim sql = _
"INSERT INTO DestinationTable (Field1, Field2, Field3) " & _
"SELECT Field1, Field2, Field3 " & _
"FROM SourceTable"
CurrentDb.Execute sql
than the equivalent using the Recordset object:
Dim rsSource As DAO.Recordset, rsDestination As DAO.Recordset
Set rsSource = CurrentDb.OpenRecordset("SourceTable")
Set rsDestination = CurrentDb.OpenRecordset("DestinationTable")
Do Until rs.EOF
rsDestination.AddNew
rsDestination!Field1 = rsSource!Field1
rsDestination!Field2 = rsSource!Field2
rsDestination!Field3 = rsSource!Field3
rsDestination.Update
rs.MoveNext
Loop
That said, using an SQL statement has its limitations:
You are limited to SQL syntax and functions.
This is partially mitigated in Access, because SQL statements can use many VBA built-in functions or functions that you define.
SQL statements are designed to work on blocks of rows. Per-row logic is harder to express using only the Iif, Choose, or Switch functions; and logic that depends on the current state (e.g. insert every other record) is harder or impossible using pure SQL. This can be easily done using the Recordset methods approach.
This too can be enabled using a combination of VBA and SQL, if you have functions that persist state in module-level variables. One caveat: you'll need to reset the state each time before issuing the SQL statement. See here for an example.
One part* of your question asked about INSERT vs. Recordset.AddNew to add one row. I suggest this recordset approach:
Dim db As DAO.Database
Dim R As DAO.Recordset
Set db = CurrentDb
Set R = db.OpenRecordset("Dessins", dbOpenTable, dbAppendOnly)
With R
.AddNew
!Contrat = rsDessin![Projet HNA].Value
!Projet = Me.Combo_Projet.Value
!AHNS = Me.Combo_Dessin.Value
.Update
.Close
End With
* You also asked how to execute an INSERT. Use the DAO.Database.Execute method which Zev recommended and include the dbFailOnError option. That will add clarity about certain insert failures. For example, a key violation error could otherwise make your INSERT fail silently. But including dbFailOnError ensures you get notified about the problem immediately. So always include that option ... except in cases where you actually want to allow an INSERT to fail silently. (For me, that's never.)
I am trying to count the number of rows in sql query result using access 2007 vba.
What I have is a text box named AGN when a user put value on it check for this value then it bring back MsgBox if the the value is already inserted. What I try to do is :
Dim rs As DAO.Recordset
Dim db As Database
Dim strSQL As String
Set db = CurrentDb
strSQL = "SELECT agencies.[agency no] FROM agencies WHERE agencies.[agency no]= " &Me.AGN.Text
Set rs = db.OpenRecordset(strSQL)
If rs.Fields.Count > 1 Then
MsgBox "this value is already here "
End If
Set rs = Nothing
When I insert any value on the textbox I got run time error 3061 (too few parameters)
The "too few parameters" error message generally means there is something in your SQL statement which Access doesn't recognize as a field, table, function or SQL keyword. In this case, it could happen if [agency no] is text rather than numeric data type. If that is the case, enclose the value of AGN with quotes when you build the SQL statement. (Or you could use a parameter query to avoid the need to quote the text value.)
strSQL = "SELECT a.[agency no] FROM agencies AS a" & vbCrLf & _
"WHERE a.[agency no]= '" & Me.AGN.Value & "'"
Debug.Print strSQL
In case of trouble, go to the Immediate window and copy the output from Debug.Print. Then you can create a new query in the Access query designer, switch to SQL View and paste in the statement text for testing.
Once your SELECT is working, you can check whether or not the recordset is empty. When it is empty both its BOF and EOF properties are true. So to detect when it is not empty, check for Not (BOF And EOF) ...
With rs
If Not (.BOF And .EOF) Then
MsgBox "this value is already here "
End If
End With
However you don't actually need to open a recordset to determine whether a matching row exists. You can check the value returned by a DCount expression.
Dim lngRows As Long
lngRows = DCount("*", "agencies", "[agency no]='" & Me.AGN.Value & "'")
If lngRows > 0 Then
MsgBox "this value is already here "
End If
Notes:
I used AGN.Value instead of AGN.Text because the .Text property is only accessible when the control has focus. But I don't know where you're using that checking code, so unsure which is the proper choice for you.
Notice the similarities between the SELECT query and the DCount options. It's often easy to translate between the two.