Using "SELECT SCOPE_IDENTITY()" in ADODB Recordset - excel

Using a VBA script in Excel, I'm trying to insert a new row into a table and then get back the identity value of that row. If I run:
INSERT INTO DataSheet(databaseUserID, currentTimestamp)
VALUES (1, CURRENT_TIMESTAMP);
SELECT SCOPE_IDENTITY()
in Management Studio, the row is inserted and it gives me the returned identity value as expected. However, when I run the exact same query through a ADODB recordset in VBA, I'm having trouble. The row is indeed inserted, but I can't access the identity value. The recordset lists 0 fields and has actually been closed as well. I've tried with and without the semicolon, and I also tried running the query as a single transaction as well. Same deal, no dice. Any idea what is going on?
Here's my VBA:
Dim rs As ADODB.Recordset
Dim cn As Connection
Dim SQLStr As String
Dim serverName As String
Dim databaseName As String
serverName = "MSSQLServer"
databaseName = "QA"
cxnStr = "Driver={SQL Server};Server=" & serverName & ";Database=" & databaseName & ";"
SQLStr = "INSERT INTO DataSheet(databaseUserID, currentTimestamp)
VALUES (1, CURRENT_TIMESTAMP); SELECT SCOPE_IDENTITY()"
Set cn = New ADODB.Connection
cn.Open cxnStr
Set rs = New ADODB.Recordset
rs.Open SQLStr, cn, adOpenKeyset, adLockOptimistic
MsgBox (rs.Fields(0).Value)
And the message box fails to display because the rs.Fields(0).Value returns NULL. I added a watch to rs, and, like I said, shows 0 fields after the query and also appears to be closed (state = 0).

When you run a batch of commands using ADODB, I believe it runs each one seperately. To force the next command to run, you have to use the following:
Set rs = rs.NextRecordset()
Changing the end of your routine to the following should do the trick:
Set rs = New ADODB.Recordset
rs.Open SQLStr, cn, adOpenKeyset, adLockOptimistic
Set rs = rs.NextRecordset
MsgBox (rs.Fields(0).Value)

You are executing two statements so you will get two results back. the recordset object can only hold one result at a time - to get the other result you need to use the NextRecordset method.
Set rs = rs.NextRecordset

In your rs.Open Try this
rs.Open SQLStr, cn, adCmdText

See what happens when you remove the adOpenKeySet and adLockOptimistic values leave them at their defaults.

Related

Export data to Access from Excel VBA

I am brand new to access and struggling to understand how to relate access table/column/row formatting to excel exporting in VBA.
I have created an access table called Data and three columns Food, Drinks, Color.
I would like to export these range of cells to Access from my excel spreadsheet:
Foodrng = Workbooks(xlFile).Sheets("ToBeExported").Range("D6")
Drinksrng = Workbooks(xlFile).Sheets("ToBeExported").Range("E6")
Colorrng= Workbooks(xlFile).Sheets("ToBeExported").Range("B12:B21")
Everything online says I should use this for because of my version:
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;"
I would like to use INSERT TO formatting to write to my database because I will be expanding the database columns quite a bit, and I feel this is the easiest way to keep track of which is going where.
strSql = "INSERT INTO Data (Food, Drinks, Color) VALUES (Foodrng, Drinksrng,Colorrng)"
I always get a syntax error when executing:
Set rs = cn.Execute(strSql)
What is the correct way to export to the Access database using the above method? Any/all information will be super helpful as I am brand new to Access
My full code:
Foodrng = Workbooks(xlFile).Sheets("ToBeExported").Range("D6")
Drinksrng = Workbooks(xlFile).Sheets("ToBeExported").Range("E6")
Colorrng= Workbooks(xlFile).Sheets("ToBeExported").Range("B12:B21")
Set cn = CreateObject("ADODB.Connection")
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source= C:\Users\User1\MyDBase.accdb"
cn.Open strConnection
strSql = "INSERT INTO Data (Food, Drinks, Color) VALUES (Foodrng, Drinksrng,Colorrng)"
Set rs = cn.Execute(strSql)
'MsgBox rs.Fields(0) & " rows in MyTable"
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
When inserting using queries, you need to pass values using parameters. I highly recommend using recordsets over insert queries.
A normal insert query can only insert one row at a time. You will need to adjust the code to insert one row at a time. You can either use a recordset, or execute a query for each row.
Foodrng = Workbooks(xlFile).Sheets("ToBeExported").Range("D6") 'Adjust ranges to select single cells
Drinksrng = Workbooks(xlFile).Sheets("ToBeExported").Range("E6")
Colorrng= Workbooks(xlFile).Sheets("ToBeExported").Range("B12:B21")
Set cn = CreateObject("ADODB.Connection")
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source= C:\Users\User1\MyDBase.accdb"
cn.Open strConnection
strSql = "INSERT INTO [Data] ([Food], [Drinks], [Color]) VALUES (?, ?, ?)"
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
With cmd
Set .ActiveConnection = cn
.CommandText = strSql
.Parameters.Append .CreateParameter(, adVarWChar, adParamInput, , foodRng) 'adVarWChar for text
.Parameters.Append .CreateParameter(, adInteger, adParamInput, , Drinksrng) 'adInteger for whole numbers (long or integer)
.Parameters.Append .CreateParameter(, adInteger, adParamInput, , Colorrng)
.Execute
End With
cn.Close
There maybe also another way, or two, perhaps worth consideration.
The first would be to set up your Excel spreadsheet as a linked table in Access - maybe it can be done with VBA. This would save you the requirement to copy the data. You might also be able to set up your target worksheet and use insert..select directly from ADODB to insert the data into Excel from Access.
The second, would be to completely avoid Access altogether if your requirements allow for this. Excel can be used as a database to some extent and supports SQL querying.
https://selectcompare.com/blogs/news/write-select-statements-for-excel-spreadsheets

Copy from recordset with VBScript

I pull data from an Oracle database using VBscript and place the recordset result into a csv file. Here is my code,
Set con = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
Set ssfile = CreateObject("Scripting.FileSystemObject").CreateTextFile("C:\Users\jasons\Documents\Closing_stock\scripts\SuperSession.csv")
con.Open "Provider=OraOLEDB.Oracle;Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=athena)(PORT=1521)))(CONNECT_DATA=(SID=jasdnf)));User Id=xxxx;Password=xxxx"
rs.Open "select * from mytable", con, 1, 3
ssfile.WriteLine "ITEM, ALTITEM"
rs.MoveFirst
Do
ssfile.WriteLine rs("item") & "," & rs("altitem")
rs.MoveNext
Loop Until rs.EOF
rs.Close
con.Close
Set con = Nothing
The code works fine. The problem I have is that there is about 4 million records and it takes the code long to loop through each record and paste it into the csv file. Is there a similar function in VBscript as in Excel-VBA such as CopyFromRecordset, where the whole recordset can be dumped into a sheet. So I would like to dump the recordset result into the csv file without having to loop through each record. How can I do this?
You could try using GetString(), which returns all (or optionally, a subset) of your recordset as a string where you can specify the delimiters etc:
Set con = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
Set ssfile = CreateObject("Scripting.FileSystemObject").CreateTextFile("C:\Users\jasons\Documents\Closing_stock\scripts\SuperSession.csv")
con.Open "Provider=OraOLEDB.Oracle;Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=athena)(PORT=1521)))(CONNECT_DATA=(SID=jasdnf)));User Id=xxxx;Password=xxxx"
rs.Open "select * from mytable", con, 1, 3
ssfile.WriteLine "ITEM, ALTITEM"
rs.MoveFirst
ssfile.writeline rs.GetString(2, , ",")
rs.Close
con.Close
Set con = Nothing
Not sure if you need to supply a rowdelimiter or not, you may need to experiment - eg try rs.GetString(2, , ",", vbNewLine)

EXCEL 2013 VBA Querying two files on separate databases on different servers at same time

I only have moderate experience when it comes to EXCEL VBA and ADO. I have two files a task file and a calendar file each of which is on a different database and a different server. I need to determine the number of actual work days associated to the task. So I need to take the timestamp date from the task file, check it against the calendar file to determine how many days in the task are actual working days. I figured out how to open two separate database connections in my VBA Script, but what I cannot figure out is how to combine the two files so I can get a working days count.
Something like this
Select Taskid, count(*)
From TaskFile, Calendar
Where TaskDate >= CalendarDate
And TaskDate <= CalendarDate
And CalendarWorkDay = 1
Group by Taskid
I thought about preloading a worksheet with the calendar data but don't see how the query will work.
Any suggestions or code snippets would be greatly appreciated.
This is a quick and dirty subroutine that will do something like what I suggested in the second comment above. It probably won't work exactly as you need it to right out of the box, but the overall idea is sound. Just realize that every record returned in the first query will generate a new query to the second file/database, so it could get pretty hairy if there are a lot of records.
Sub twoRecordsets()
Dim objConn As ADODB.Connection, objConn2 As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strSQL As String
Dim strConn As String, strconn2 As String
'open first connection
Set objConn = New ADODB.Connection
strConn = "<your 1st connection string>"
objConn.Open strConn
'open second connection
Set objConn2 = New ADODB.Connection
strconn2 = "<your 2nd connection string>"
objConn2.Open strconn2
'first query:
strSQL = "Select Taskid, TaskDate From TaskFile GROUP BY TaskID"
'open first recordset using first query
Set rs = New ADODB.Recordset
rs.Open strSQL, objConn
'Die if there are no records returned
If rs.EOF And rs.BOF Then
Exit Sub
End If
'Loop through recordset
rs.MoveFirst
Do Until rs.EOF
'build a sql statement to do the second bit. might have to monkey with quote marks and date formats depending on DB
strSQL = "SELECT count(*) as recordcount FROM calendar where '" & rs.Fields("TaskDate").Value & "' >= CalendarDate And '" & rs.Fields("TaskDate").Value & "' <= CalendarDate And CalendarWorkDay = 1"
'open recordset
Set rs2 = New ADODB.Recordset
rs2.Open strSQL, objConn2
'Get your answer from the return
heresYourAnswer = rs2.Fields("recordcount").Value
'Iterate to next record in rs
rs.MoveNext
Loop
End Sub

Error when running a parameter query from access 2007 in excel vba

I'm trying to run a query in an Access 2007 database from an Excel 2007 VBA script. The Access query has parameters called "Year" and "Month".
I'm trying to get the following code to work:
Sub RunMyQuery()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim dbPath As String
Dim stQRY As String
Dim stCon As String
Dim cmd As New ADODB.Command
Dim prmYear As New ADODB.Parameter
Dim prmMonth As New ADODB.Parameter
dbPath = "<PATH_TO_MY_DB>"
stCon = "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "Data Source=" & dbPath & ";"
cn.Open (stCon)
cn.CursorLocation = adUseClient
Set cmd.ActiveConnection = cn
Set prmYear = cmd.CreateParameter("Year", adNumeric, adParamInput, , 2011)
Set prmMonth = cmd.CreateParameter("Month", adNumeric, adParamInput, , 5)
cmd.Parameters.Append prmYear
cmd.Parameters.Append prmMonth
cmd.CommandText = "SELECT * FROM [Month_Totals]"
cmd.CommandType = adCmdTable
Set rs = cmd.Execute
Sheets("Sheet1").Range("A1").CopyFromRecordset rs
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
When I run this, the code stops on "cmd.Execute" with
Run-time error '-214217900 (80040e14)':
Syntax error in FROM clause.
What am I getting wrong?
The command text seems simple enough to me. Am I missing something there?
Am I misusing the parameters functionality of ADODB.Command? I don't think that's the problem here, because I've tried running this same script with a non-parametrized query substituted for Month_Totals, and gotten the same error.
I believe The parameters are only for use when you are using a saved query in access. I would solve your problem by moving the parameters into the SQL statment.
Change
"SELECT * FROM [Month_Totals]"
to
"SELECT * FROM [Month_Totals] WHERE Year = 2011 AND Month = 5"
The reason that this is returning an error is that command type is set to adCmdtable, but the command does not reference a table, it references an SQL string, so:
cmd.CommandType = adCmdText
Next, in order to return specific data, you need to include a WHERE clause, with field names in the correct order for the parameters:
cmd.CommandText = "SELECT * FROM [Month_Totals] Where [Year]=? AND [Month]=?"

Clearing a table

What I'm trying to do is, while in Excel, use VBA to push data to an existing Access table. I've been able to do this, but am having one small hiccup. Before I push the data to access, I want to clear the current data on the Access table, so when the new data from Excel comes in, it is the only data in the Access table. I really don't know how to write code for Access since the class has been on VBA for Excel. I've tried several different approaches and each time it doesn't work. For example, the code that seemed like it should work is
DoCmd.RunSQL "DELETE tblName.* FROM CoversheetTableFourthAttempt
but I get an error telling me to define an object.
If you could help me with this, I would really appricate it
I've put my code below for reference.
Sub AccessFourthMonth()
Dim cn As ADODB.Connection, rs As ADODB.Recordset, r As Long
' connect to the Access database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; " & _
"Data Source=C:\Users\Kent\Documents\MBA\Winter 2009 Semester\MBA 614\Final Project\shilded\testdatabase.mdb"
' open a recordset
Set rs = New ADODB.Recordset
rs.Open "CoversheetTableFourthAttempt", cn, adOpenKeyset, adLockOptimistic, adCmdTable
' all records in a table
r = 2 ' the start row in the worksheet
Do While Len(Range("A" & r).Formula) > 0
' repeat until first empty cell in column A
With rs
.AddNew ' create a new record
' add values to each field in the record
.Fields("Project") = Range("A" & r).Value
.Fields("Description") = Range("B" & r).Value
.Fields("Amount") = Range("C" & r).Value
.Fields("Date") = Range("D" & r).Value
.Update ' stores the new record
End With
r = r + 1 ' next row
Loop
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
Try
DoCmd.RunSQL "DELETE * FROM TableName"
This article might be of interest: Executing SQL Statements in VBA Code
Try the following from Excel:
dim cn as adodb.connection
dim cmd as adodb.command
set cn = new adodb.connection
cn.open "put your connection string here"
set cmd = new adodb.command
cmd.commandtype = adcmdtext
cmd.commandtext = "Delete * from myTable"
cmd.activeconnection = cn.connectionstring
cmd.execute
DoCmd is internal to Access application and not recognized by Excel application.
Simple approach to your problem is to fire the delete query from Excel itself.
Add this part after your cn.Open "Provider.. line
cn.Execute "DELETE * FROM CoversheetTableFourthAttempt"
This should clear the table before next part which fills the data runs.
Your DoCmd approach has two problems. You used a quote to start a string, but didn't include a closing quote. But even with proper quoting, your DoCmd won't work because Excel does not know that CoversheetTableFourthAttempt is the name of a table in an Access database.
You showed that you can successfully create an ADO connection to your Access database. So my suggestion is to use the Execute method of the connection object to execute your SQL statment:
cn.Execute "DELETE FROM CoversheetTableFourthAttempt;"
Finally, visit Problem names and reserved words in Access to understand why Date, Description, and Project are not great choices for Access field names.

Resources