I'm trying to insert some data into SQL from Excel VBA. The SQL commands are built up over the course of the VBA script, and include the use of some SQL variables.
I'm trying to understand how transactions work in VBA, and whether they will work with what I need to do, I have the code below that will test this, but it does not work. It always gives me an error about "Must define scalar variable #name" so I assume there is an issue here with the scope of the data/transaction. How can I get this simple code to work?
Const stADO As String = "Provider=SQLOLEDB.1;Integrated Security=SSPI;" & _
"Persist Security Info=False;" & _
"Initial Catalog=ImportTest;" & _
"Data Source=localhost\sqlexpress"
Set cn = New ADODB.Connection
With cn
.CursorLocation = adUseClient
.Open stADO
.CommandTimeout = 0
End With
cn.BeginTrans
cn.Execute "set implicit_transactions off"
cn.Execute ("declare #name varchar(100)")
cn.Execute ("set #name='name'")
cn.Execute ("Insert into test (id,name) values (55,#name)")
cn.CommitTrans
cn.Close
Set cn = Nothing
you need to execute all these in 1 batch
cn.Execute "set implicit_transactions off"
cn.Execute ("declare #name varchar(100)")
cn.Execute ("set #name='name'")
cn.Execute ("Insert into test (id,name) values (55,#name)")
built a string and then use 1 cn.Execute
Better yet, use parameterized queries to guard against SQL injection
Related
I wanted to get the insert ID of the the last insert query
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Set cn = CurrentProject.Connection
cn.Execute "Insert into Locations(locationName) Values('" & Me.location.Value & "')"
rs.Open "SELECT ##identity AS locationID FROM locations", cn
Debug.Print rs.Fields("locationID")
However, this always returns 0 . Is there a way to get the insertID using the ADO connection?
Edit1:
With the help of the reply below and further research I found out the soultion
I forgot to mention that my backend database is mysql database not MSSQL or Access. I also changed my code to use global connection now which is named g_cn. I think currentproject.connection does not work with backend mysql database to get the insertionID because it uses DAO connection.
I set the global connection by doing:
g_cn.Open "DRIVER={MySQL ODBC 8.0 Unicode Driver};Server=" & gc_DevServerName & ";Database=" & gc_DevDBName & ";Uid=" & gc_DevUser & ";Pwd=" & gc_DevPassword & ";Option=3;"
I can now use ADO. So my code changed to :
Dim rs As New ADODB.Recordset
rs.Open "Insert into Locations(locationName) Values('" & Me.location.Value & "')", g_cn
rs.Open "Select LAST_INSERT_ID() as last from locations;", g_cn
Debug.Print rs.Fields("last")
rs.Close
SELECT ##identity is a special query in Access. You can't use it with a FROM clause. Remove that and the query will work:
rs.Open "SELECT ##identity AS locationID", cn
I am trying to retrieve data to excel form a database in MS access. However the recordcount property for recordset always return -1 though for other purposes the code is working fine.
The code I am using is as follows :
`Sub datarecordset()
Dim cn As adodb.Connection
Dim oRs As adodb.Recordset
Set cn = CreateObject("ADODB.Connection")
DBPath = "C:\[databse path]" & "\[database name].accdb"
dbWs = "[excel sheet name]"
scn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DBPath
dsh = "[" & "[excel sheet name]" & "$]"
cn.Open scn
Dim sSQL As String
Dim F As Integer
sSQL = "Select 'W',a.[Subledger],NULL,sum(a.[Amount]) from GL_Table a where a.[Opex_Group] = 10003 and year(a.[G/L Date]) = " & Year(Sheets("Repairs").Cells(1, 4)) & " and month(a.[G/L Date]) = " & Month(Sheets("Repairs").Cells(1, 4))
sSQL = sSQL & " group by " & "a.[Subledger],(year(a.[G/L Date])),(month(a.[G/L Date]))"
Set oRs = cn.Execute(sSQL)
Debug.Print oRs.RecordCount
oRs.Close
....... further code to print to excel here
cn.Close
End Sub`
The code will fetch data in recordset and write in excel. But since the recordset property is not returning the recordcount so can't print values of various fields in recordset to different cells of excel worksheet.
I searched on google and understood that I need to declare the recordset type and for that I have to use connection.open in place of connection.execute. But I am trying to change the code then it gives the error object variable or With variable not defined.
Any quick help will be welcome. Thanks.
The link by #BitAccesser provides a valid solution. Quick how-to-implement in your situation:
Instead of Set oRs = cn.Execute(sSQL)
Set oRS = CreateObject("ADODB.Recordset")
oRS.CursorLocation = adUseClient
oRS.Open sSQL, cn
ADO's recordcount property returns -1 when ADO cannot determine the number of records or if the provider or cursor type does not support RecordCount. That last one is true for this case.
To avoid this, there are several solutions. The most simple one is to use a client-side cursor, which I just demonstrated, but more alternative solutions are provided in the links by #BitAccesser
You may also specify the CursorType as the third argument to open the RecordSet as follows, which is optional
The first two lines, leaving blank or selecting adOpenDynamic, do not give the record count.
The remaining ones work OK.
1-RS.Open SqlStr, Conn
2-RS.Open SqlStr, Conn, adOpenDynamic
(Erik's solution)
- 3-RS.CursorLocation = adUseClient
Other Options also work fine, please note 4- and 6- which do not require a seperate line
- 4-RS.Open SqlStr, Conn, adOpenKeyset
- 5-RS.Open SqlStr, Conn, adOpenKeyset AND RS.CursorLocation = adUseClient
- 6-RS.Open SqlStr, Conn, adOpenStatic AND RS.CursorLocation = adUseClient
- 7-RS.Open SqlStr, Conn, adOpenStatic
BR, Çağlar
You can still use the Execute method but you need to set the correct cursor type. The recordset is created automatically with cursor type adOpenForwardOnly. This results in oRs.RecordCount = -1. adOpenKeySet is the correct cursor type to correctly show oRs.RecordCount.
Note: The LockType is irrelevant in this case.
Set oRs = cn.Execute(sSQL)
oRs.Close
oRs.CursorType = adOpenKeyset
oRs.Open
Debug.Print oRs.RecordCount
Closing the recordset, changing the cursor type and reopening the recordset worked fine for me (Access 2016 on Windows 7).
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
I have an SP in SQL Server (2012). It returns a single row...verified using SSMS
exec dbo.cpas_DeleteProjects N'3555,3565'
It returns a valid row with record count info in the columns.
From Access(2007), I'm calling it using an adodb call:
Sub DevolveProjects(ProjectIDs As String)
''
' Looking for a comma-delimited list of project IDs in string form: "34,52,14"
'
Dim cnn As New adodb.Connection
Dim rstReturnValues As adodb.Recordset
cnn.ConnectionString = "Provider=SQLOLEDB;Server=" & CurrentServerName() & ";Initial Catalog=" & CurrentDBName() & ";Integrated Security=SSPI;"
cnn.Open
Set rstReturnValues = cnn.Execute("exec dbo.cpas_DeleteProjects N'" & ProjectIDs & "'")
With rstReturnValues...
Problem is, rstReturnValues is closed. No data.
I've verified using SQL Profiler that the sp is running the expected SQL when called from the Access app as above, and seems to be performing just fine, so there are no issues with authentication. I'm just not getting my recordset populated. When I copy and paste the exact SQL statement SQL Profiler saw into an SSMS query, it returns EXACTLY what I expect....
Have I coded the .execute call incorrectly? Ideas?
The code looks right, although you could try using bind parameters instead of a single SQL string. For something quick and dirty it doesn't make any difference, but this removes the possibility of SQL injection
Dim lConnection as new ADODB.Connection
Dim lCommand as new ADODB.Command
Dim lParameter as ADODB.Parameter
Dim lRecordset as ADODB.Recordset
lConnection.ConnectionString = "Provider=SQLOLEDB;Server=" & CurrentServerName() & ";Initial Catalog=" & CurrentDBName() & ";Integrated Security=SSPI;"
lConnection.Open
lCommand.ActiveConnection = lConnection
lCommand.CommandText = "dbo.cpas_DeleteProjects"
lCommand.CommandType = adCmdStoredProc
set lParameter = lCommand.CreateParameter(, adVarWChar, adParamInput, 200, ProjectIDs) 'replace 200 with parameter length, different syntax if nvarchar(max)
lCommand.Parameters.Append lParameter
Set lRecordset = lCommand.Execute()
Debug.Print lRecordset(0) 'Does the recordset contain anything?
...
lRecordset.Close
lConnection.Close
Using the connection string below I can connect to a SQL2000 DB but not a SQL2005. I have the code in an ASP file.
Dim connStr, cn, rs, sql
connStr = "Provider=SQLOLEDB;Persist Security Info=True" _
& ";Initial Catalog=mydatabase" _
& ";User ID=dbuser" _
& ";Password=dbpwd" _
& ";Data Source=servername"
sql = "SELECT TOP 1 [Column1] FROM [dbo].[MyTable] order by NEWID()"
Set cn = Server.CreateObject("ADODB.Connection")
cn.Open cn
set rs= server.CreateObject("ADODB.Recordset")
rs.CursorLocation=3
rs.Open sql,cn,3,4
if not rs.EOF then
Response.Write("<b>Column1: " & rs("Column1") & "</b><br />")
end if
set rs.ActiveConnection= nothing
rs.Close
set rs= nothing
if ucase(TypeName(cn)) = "CONNECTION" then
cn.Close
Set cn = Nothing
end if
I have even tired with SQLOLEDB.1
Sql login is enabled on the sql server.
Error: The connection cannot be used to perform this operation. It is either closed or invalid in this context.
Happens on rs.Open sql,cn,3,4
It happens to everybody sometime:
Dim connStr, cn, rs, sql
connStr = "Provider=SQLOLEDB;Persist Security Info=True" _
& ";Initial Catalog=mydatabase" _
& ";User ID=dbuser" _
& ";Password=dbpwd" _
& ";Data Source=servername"
sql = "SELECT TOP 1 [Column1] FROM [dbo].[MyTable] order by NEWID()"
Set cn = Server.CreateObject("ADODB.Connection")
cn.Open connStr
You are calling a variable conn as connection string but you have declared and filled connStr
Change "cn.Open conn" with "cn.Open connStr"
What is the error? SQL Server 2005/8 install with remote connections disabled -- check this support article.
I see, try setting your connString on your Connection object (you use conn instead of connStr). Uee option explicit to avoid these errors.