Hello I am trying to move an Excel worksheet to an Access database, both of which have identical field names. The Code is written in Excel.
The user inputs several form fields and upon clicking finish, a seperate worksheet is updated. Then, the update Access subroutine is called to update the database with the contents of the sheet. However I continue to receive the following error:
Run-time error '-2147417848 (80010108)':
Automation error
The object invoked has disconnected from its clients.
I googled the error and can't quite see what is going on. Here is my code:
Sub Update_Access_fromExcel()
' exports data from the active worksheet to a table in an Access database
Dim cn As ADODB.Connection, rs As ADODB.Recordset, r As Long
' Connect to Access database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; " & "Data Source=D:\Tool_Database\Tool_Database.mdb;"
' open the recordset
Set rs = New ADODB.Recordset
rs.Open "Project_Names", cn, adOpenKeyset, adLockOptimistic, adCmdTable
r = 2
Do Until IsEmpty(Worksheets("NewProj").Cells(r, 1))
With rs
.AddNew
.Fields("Proj_Name") = Worksheets("NewProj").Cells(r, 1).Value
.Update
End With
r = r + 1
Loop
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
Also, is there a way I could just add the new info to the end of the Access database?
Thanks for your help.
Related
I have to create over 170 named ranges in Excel which I am trying to load into an Access table. Below is my code.
Sub Load_To_ALLL_TSD()
Dim strDatabasePath As String
Dim oApp As Access.Application
Dim PathOfworkbook As String
PathToDB = ThisWorkbook.Path
strDatabasePath = PathToDB & "\RAROC.accdb"
Set oApp = CreateObject("Access.Application")
'Set db = Application.CurrentProject
oApp.Visible = True
oApp.OpenCurrentDatabase strDatabasePath
Set db = CurrentDb()
Set rs = db.OpenRecordset("ALLL_TSD", dbOpenTable)
With oApp
With rs
.AddNew ' create a new record
' add values to each field in the record
.Fields("TSD_Base_Rate_Received") = Range("TSD_Base_Rate_Received").Value
.Fields("TSD_Base_Rate_Received_Input") = Range("TSD_Base_Rate_Received_Input").Value
.Fields("TSD_Calculated_RAROC") = Range("TSD_Calculated_RAROC").Value
.Fields("TSD_Capital_Factor") = Range("TSD_Capital_Factor").Value
' etc, etc, lot more fields and named ranges here
' add more fields if necessary...
.Update ' stores the new record
End With
End With
Set oApp = Nothing
MsgBox ("Done! All Data saved to RAROC database!!")
End Sub
I'm getting some weird errors! If I run the code using F8, it works fine. If I click a button to fire the code, sometimes it works and sometimes it doesn't work. I has errored out on several different lines.
Once it threw an error here:
Set rs = db.OpenRecordset("ALLL_TSD", dbOpenTable)
Error reads 'object variable or with block not set'
Once it said 'Microsoft Access has stopped working' and it threw an error on this line.
.Fields("TSD_Base_Rate_Received_Input") = Range("TSD_Base_Rate_Received_Input").Value
I've seen some other weird things too.
I have a reference set to both:
Microsoft DAO 3.6 Object Library
Microsoft Access 14.0 Object Library
It almost seems like I'm establishing a connection to Access and then almost immediately I lost the connection, somehow.
Finally, I have no Forms or Reports, and the DB is not split. I have just one single table in there now, which I am trying to write to. What can I try to resolve this?
Here's a basic example without using Access.
Needs a reference to Microsoft ActiveX Data Objects 2.x Library
Sub Tester()
Dim con As New ADODB.Connection, rs As New ADODB.Recordset
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "Data Source = " & ThisWorkbook.Path & "\RAROC.accdb"
'get an empty recordset to add new records to
rs.Open "select * from [ALLL_TSD] where false", con, _
adOpenDynamic, adLockBatchOptimistic
With rs
.AddNew
.Fields("TSD_Base_Rate_Received") = Range("TSD_Base_Rate_Received").Value
.Fields("TSD_Base_Rate_Received_Input") = Range("TSD_Base_Rate_Received_Input").Value
.Fields("TSD_Calculated_RAROC") = Range("TSD_Calculated_RAROC").Value
.Fields("TSD_Capital_Factor") = Range("TSD_Capital_Factor").Value
'etc...
.UpdateBatch '<< EDIT
.Close
End With
con.Close
End Sub
Let me start of by saying I am new to both this site as well as VBA so please bear with me. Thank you in advance for your help.
I have a VBA function that runs an existing query from Access. Some of the tables being queried are stored in an Oracle database that require a user specific password access. Right now, a sub that I wrote to automate a report calls this function 7 times and requires the user to input their Oracle password each time the function is called (it also stops the sub and gives an error message if they type in the password incorrectly which I see as a likely event if they need to do it 7 times). The code works but I would like to find a way to have the code ask for the password once and be done with it. All of the solutions I have found involve connecting to and querying Oracle directly which requires very complicated SQL coding that I am by no means capable of writing.
I am also having an issue where the columns show up in the excel sheet in a different order than they do in Access for some of the queries. This seems to be consistent so it isn't to big of a problem but I would like to know how to prevent this to prevent any future issues.
Here is the code for the function I am currently using. Any insight would be greatly appreciated!
Option Explicit
'Single Argument "qryName" as string. Runs access qry and copys recordset to active sheet.
Function AccessQueryPull(qryName As String)
'Declare variables
Dim rs As ADODB.Recordset
Dim cn As ADODB.Connection
'open the connection to the access database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=filePath.accdb;"
'format the command to run the query
Dim cmd As New ADODB.Command
cmd.CommandType = adCmdStoredProc
cmd.CommandText = qryName
cmd.ActiveConnection = cn
'execute the query
Set rs = New ADODB.Recordset
Set rs = cmd.Execute()
'copy data to excel sheet
ActiveSheet.Cells(2, 1).CopyFromRecordset rs
'Cleanup
rs.Close
Set rs = Nothing
The reason you are prompted each time for Oracle credentials is that you create a fresh, new connection to MS Access each time the function is called.
Simply persist the MS Access connection by connecting once in the Sub that calls function and pass connection object as parameter. In this way, any connection error is caught in the parent routine. As for column order simply declare the columns in the needed order in SQL statement.
Sub RunQueries()
Dim rs As ADODB.Recordset
Dim cn As ADODB.Connection
'open the connection to the Access database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=filePath.accdb;"
Call AccessQueryPull(cn, "SELECT Col1, Col2, Col3 FROM Qry1") ' AND OTHER 6 CALLS
cn.Close
Set cn = Nothing
End Sub
Function AccessQueryPull(accConn As ADODB.Connection, qryName As String)
'format the command to run the query
Dim cmd As New ADODB.Command
cmd.CommandType = adCmdStoredProc
cmd.CommandText = qryName
cmd.ActiveConnection = accConn
'execute the query
Set rs = New ADODB.Recordset
Set rs = cmd.Execute()
'copy data to excel sheet
ActiveSheet.Cells(2, 1).CopyFromRecordset rs
'Cleanup
rs.Close
Set rs = Nothing: Set cmd = Nothing
End Function
I'm putting together an Excel front-end with an Access database behind it, so that some of my colleagues can collate data from previous projects (without having to learn to use Access themselves!)
So I have a "Data" sheet in the Excel workbook that occasionally refreshes from the database, then a "Dashboard" sheet that allows filtering of that data.
Where I'm having trouble is in closing the loop - allowing input of new data into the database. The relevant code is below:
Private Sub btnDBImport_Click()
Dim rst As ADODB.Recordset
Dim errorList As String
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
With cn
.Provider = "Microsoft.ACE.OLEDB.16.0"
.ConnectionString = "\\FileAddress\DB.accdb"
.Open
End With
Set rst = New ADODB.Recordset
rst.CursorLocation = adUseServer
rst.Open Source:="TableName", _
ActiveConnection:=cn, _
CursorType:=adOpenDynamic, _
LockType:=adLockOptimistic, _
Options:=adCmdTableDirect
If validationTest = False Then
errorList = errorList & "Data at row " & i & " was not added to the database due to missing information." & vbCrLf
Else
rst.AddNew
With rst
.Fields(1) = Value1
.Fields(2) = Value2
End With
rst.Update
End If
Next i
If errorList <> "" Then
MsgBox errorList, vbOKOnly, "Data error"
End If
rst.Close
Set rst = Nothing
cn.Close
Set cn = Nothing
End Sub
The above is everything involved in defining, opening and working on a recordset; I get an error at rst.AddNew because "cannot update, database or object is ReadOnly".
Any help appreciated.
So, with thanks to the comment from Jimmy Smith, I've got to the bottom of this, or at least fixed my immediate problem.
The table was being treated as ReadOnly both when accessed through MS Access directly and when loaded as a Recordset using VBA because there was a link to the table in the same Excel workbook.
Now that I've deleted the link to the table and instead update the records in it through code, I'm consistently getting requested levels of access to the relevant table and everything is working as intended. Time to commit to Rubberduck and show it to the userbase.
I am working on an Excel application that queries a SQL database. The queries can take a long time to run (20-40 min). If I've miss-coded something it can take a long time to error or reach a break point. I can save the results to a sheet fine, it's when I am working with the record sets that things can blow up.
Is there a way to load the data into a ADODB.Recordset when I'm debugging to skip querying the database (after the first time)?
Would I use something like this?
Query Excel worksheet in MS-Access VBA (using ADODB recordset)
I had to install the MDAC to get the msado15.dll and once I had it I added a reference to it from (on Win7 64bit):
C:\Program Files (x86)\Common Files\System\ado\msado15.dll
Then I created a function to return an ADODB.Recordset object by passing in a sheet name that exists in the currently active workbook. Here's the code for any others if they need it, including a Test() Sub to see if it works:
Public Function RecordSetFromSheet(sheetName As String)
Dim rst As New ADODB.Recordset
Dim cnx As New ADODB.Connection
Dim cmd As New ADODB.Command
'setup the connection
'[HDR=Yes] means the Field names are in the first row
With cnx
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString = "Data Source='" & ThisWorkbook.FullName & "'; " & "Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'"
.Open
End With
'setup the command
Set cmd.ActiveConnection = cnx
cmd.CommandType = adCmdText
cmd.CommandText = "SELECT * FROM [" & sheetName & "$]"
rst.CursorLocation = adUseClient
rst.CursorType = adOpenDynamic
rst.LockType = adLockOptimistic
'open the connection
rst.Open cmd
'disconnect the recordset
Set rst.ActiveConnection = Nothing
'cleanup
If CBool(cmd.State And adStateOpen) = True Then
Set cmd = Nothing
End If
If CBool(cnx.State And adStateOpen) = True Then cnx.Close
Set cnx = Nothing
'"return" the recordset object
Set RecordSetFromSheet = rst
End Function
Public Sub Test()
Dim rstData As ADODB.Recordset
Set rstData = RecordSetFromSheet("Sheet1")
Sheets("Sheet2").Range("A1").CopyFromRecordset rstData
End Sub
The Sheet1 data:
Field1 Field2 Field3
Red A 1
Blue B 2
Green C 3
What should be copied to Sheet2:
Red A 1
Blue B 2
Green C 3
This is saving me a HUGE amount of time from querying against SQL every time I want to make a change and test it out...
--Robert
Easiest would be to use rs.Save "filename" and rs.Open "filename" to serialize client-side recordsets to files.
Another alternative to get a Recordset from a Range would be to create and XMLDocument from the target Range and open the Recordset from that document using the Range.Value() property.
' Creates XML document from the target range and then opens a recordset from the XML doc.
' #ref Microsoft ActiveX Data Objects 6.1 Library
' #ref Microsoft XML, v6.0
Public Function RecordsetFromRange(ByRef target As Range) As Recordset
' Create XML Document from the target range.
Dim doc As MSXML2.DOMDocument
Set doc = New MSXML2.DOMDocument
doc.LoadXML target.Value(xlRangeValueMSPersistXML)
' Open the recordset from the XML Doc.
Set RecordsetFromRange = New ADODB.Recordset
RecordsetFromRange.Open doc
End Function
Make sure to set a reference to both Microsoft ActiveX Data Objects 6.1 Library and Microsoft XML, v6.0 if you want to use the example above. You could also change this function to late binding if so desired.
Example call
' Sample of using `RecordsetFromRange`
' #author Robert Todar <robert#roberttodar.com>
Private Sub testRecordsetFromRange()
' Test call to get rs from Range.
Dim rs As Recordset
Set rs = RecordsetFromRange(Range("A1").CurrentRegion)
' Loop all rows in the recordset
rs.MoveFirst
Do While Not rs.EOF And Not rs.BOF
' Sample if the fields `Name` and `ID` existed in the rs.
' Debug.Print rs.Fields("Name"), rs.Fields("ID")
' Move to the next row in the recordset
rs.MoveNext
Loop
End Sub
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.