I have an access file with over 3 milion records. I would would like that excel will be able to perfrom a vlookup serach in the access table and give me the result on excel. Any simple way to do that?
Thank's!
That would be a terribly inefficient way to do it.
In a database you can use queries with JOINs to do this. If you tell us, what the table looks like and what you want to achieve, we can give you more info.
As Andre451 said - you need SQL rather than Excel functions to get data out of the database.
This code will dump the query results into the immediate window. It should show you how to extract data from Access though.
Note - I usually make the oDB variable global if I'm planning on querying the database a lot. Also without knowing the name of your database or table structure I've just shown an example of the query used - you'll have to add code to get the employee number and year from the user.
Tip - Write your query in Access using the query editor, view it as SQL and then copy to Excel.
Sub ReturnValues()
Dim oDB As Object
Dim rst As Object
Set oDB = GetDatabaseReference
Set rst = CreateObject("ADODB.RecordSet")
With rst
.CursorType = 2
.Open "SELECT Salary FROM tbl_SomeTable WHERE Emp=1 AND Year=2015", oDB
If Not rst Is Nothing Then
If Not .BOF And Not .EOF Then
.MoveFirst
Do
Debug.Print rst.Fields("Salary")
.MoveNext
Loop While Not .EOF
End If
End If
End With
End Sub
Public Function GetDatabaseReference() As Object
Dim cn As Object
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Only set a reference to the database if it doesn't already exist. '
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If oDB Is Nothing Then
Set cn = CreateObject("ADODB.Connection")
Select Case Val(Application.Version)
Case 11
'Access 2003
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source =C:<Full Path To Database>.mdb;"
'Could use:
'"Data Source=" & ThisWorkbook.Path & "\Database\Database.mdb"
Case 14
'Access 2010
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source =C:<Full Path To Database>.mdb;" & _
"Persist Security Info=False;"
''Could use:
'"Data Source=" & ThisWorkbook.Path & "\Database\Database.mdb;" & _
'"Persist Security Info=False;"
End Select
If Not cn Is Nothing Then
Set GetDatabaseReference = cn
End If
Else
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'oDB already has a reference, so ensure it's maintained. '
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Set GetDatabaseReference = oDB
End If
End Function
Related
I've found lots of posts on this problem, but so far no solutions have helped.
I'd like to read and write data from/to an Excel worksheet from an external VBA application - so far it reads OK, but I get an error while trying to write values to the ADODB Recordset.
Here's the code:
Sub UpdateFromExcel()
'https://stackoverflow.com/questions/15620080/reading-data-using-oledb-from-opened-excel-file
Dim oConn As New ADODB.Connection
Dim oRS As New ADODB.Recordset
Dim sPath
Dim sSQL As String
sSQL = "SELECT * FROM [Sheet1$A1:N10000]"
sPath = frmExcelSync.txtFilePath
oConn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" & sPath & "';" & _
"Extended Properties='Excel 12.0;HDR=Yes;IMEX=1';"
oRS.Open sSQL, oConn, adOpenDynamic, adLockOptimistic
Do While Not oRS.EOF
'ITERATE THROUGH EACH ROW HERE
'ADD CODE ABOVE
'****GET AN ERROR HERE:****
oRS.Update "Occurrence Name", "Test"
oRS.MoveNext
Loop
oRS.Close
oConn.Close
End Sub
The error is
"Cannot update. Database or object is read-only".
I've tried different lock and cursor types, and I've tried editing the fields then using the .update method, but nothing has worked so far.
Any suggestions?
your update statement is not correct. I believe you want to update the column "Occurrence Name" with the value "Test"
What you should write is.
Do While Not oRS.EOF
oRS![Occurrence Name].value = "Test"
oRS.MoveNext
Loop
oRS.Update
The problem seems to have gone away somehow.
I tried a few different things (different spreadsheets) with mixed success then restarted the application - now it works.
No code changes at all.
I am writing a small application in Excel 2002 and I need to store numbers in some format, it can be a string.
The tables I have a 1:1 relationship and other table is just a table of one column so using access is not necesary and having to have another file is something I'd like to avoid.
So, I want to store it in separate sheets.
However, I like the benefits of SQL for querying and I need it.
I tried using some ADODB connection strings to reach this but I cannot achieve it.
I used the following code:
Dim cn As Object, rs As Object, output As String, sql As String
'---Connecting to the Data Source---
Set cn = CreateObject("ADODB.Connection")
With cn
.Provider = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MyExcel.xls;"
.ConnectionString = "Data Source=" & ThisWorkbook.FullName & ";" & "Excel 8.0;HDR=Yes;IMEX=1"
.Open
End With
Also, do I have to use ODBC or should I use OLE DB? I don't know if OLE DB could be used to query in excel files.
Also, is it possible to do inserts with SQL using this ODBC or OlE DB?
I tried different providers in the connection string, and I checked the ADO references to be available.
Also, I get this error:
"Error 3706. The specified provider could not be found. It may not be installed properly."
Connection issue
First, there was an error in your Provider string, it should not contain the part with Data Source=C:\MyExcel.xls; since this is part of the connection string. So it should look like this:
.Provider = "Provider=Microsoft.Jet.OLEDB.4.0;"
.ConnectionString = "Data Source=" & ThisWorkbook.FullName & ";" & "Excel 8.0;HDR=Yes;IMEX=1"
ODBC vs OLEDB
I've never used ODBC, but based on this answer, you can't use it to query an Excel file, so OLEDB is the way to go.
Insert Statement
Once you have a working ADODB connection, insert query should work as hoped. I'm providing an example below that worked for me, but there is a few caveats:
I'm using the ACE.OLEDB.12.0 instead of JET.OLEDB.4.0 with Excel for Microsoft 365 MSO (Version 2112 Build 16.0.14706.20000) 64-bit on Windows 10.
I'd suggest to set Mode=ReadWrite in your connection string to avoid potential writting permission issues (but it might work even without it.).
Regarding the IMEX setting, I was having errors when it was set to IMEX=1, so I switched to IMEX=0 (see related question.
The example
With a workbook named Data.xls with the first sheet named Data and the following data :
Data for copy-paste
I can run the following:
Dim wb As Workbook
Set wb = Workbooks("Data.xls")
Dim ws As Worksheet
Set ws = wb.Worksheets("Data")
'Create connection
Dim conn As Object
Set conn = CreateObject("ADODB.Connection")
With conn
.Provider = "Microsoft.ACE.OLEDB.12.0;"
.ConnectionString = "Data Source=" & wb.FullName & ";" & "Excel 8.0;HDR=Yes;IMEX=0;Mode=ReadWrite;"
.Open
End With
'Compose the INSERT statement.
Dim query As String
Const sep = ", "
query = "INSERT INTO [" & ws.Name & "$] " & _
"(Id, Name, Age) " & _
" VALUES (" & _
4 & sep & _
"'" & "Joe" & "'" & sep & _
40 & _
")"
'Execute the statement.
conn.Execute query, adCmdText
'Close the connection
conn.Close
And it should insert the data as follow:
Should you use ACE or JET?
If JET works for you, you might as well use it. Based on this article , you should also have the 32-bit version of ACE available with Windows 7 to work with Excel 2002 (32-bit), but based on your comment it seems like it's causing some problems.
See also some interesting answer about JET vs ACE.
I am having trouble getting data from an Access Database. I found this code online, and it seems to work (to an extent), but for some reason it will only pull the column headers, and none of the data from the query. I am not too familiar with Access, that is why I pulled one from offline.
Someone had a similar post a while back, where the code they used was the same, and our queries were exactly the same, but we had different issues.
Importing Data From Access Using Excel VBA
Would anyone happen to know why the data won't pull?
Sub getDataFromAccess()
Dim DBFullName As String
Dim Connect As String, Source As String
Dim Connection As ADODB.Connection
Dim Recordset As ADODB.Recordset
Dim Col As Integer
Dim startdt As String
Dim stopdt As String
Dim refresh
refresh = MsgBox("Start New Query?", vbYesNo)
If refresh = vbYes Then
Sheet1.Cells.Clear
startdt = Application.InputBox("Please Input Start Date for Query (MM/DD/YYYY): ", "Start Date")
stopdt = Application.InputBox("Please Input Stop Date for Query (MM/DD/YYYY): ", "Stop Date")
DBFullName = "X:\MyDocuments\CMS\CMS Database.mdb"
' Open the connection
Set Connection = New ADODB.Connection
Connect = "Provider=Microsoft.ACE.OLEDB.12.0;"
Connect = Connect & "Data Source=" & DBFullName & ";"
Connection.Open ConnectionString:=Connect
Set Recordset = New ADODB.Recordset
With Recordset
Source = "SELECT * FROM Tracking WHERE [Date_Logged] BETWEEN " & startdt & " AND " & stopdt & " ORDER BY [Date_Logged]"
.Open Source:=Source, ActiveConnection:=Connection
For Col = 0 To Recordset.Fields.Count - 1
Range(“A1”).Offset(0, Col).Value = Recordset.Fields(Col).Name
Next
Range(“A1”).Offset(1, 0).CopyFromRecordset Recordset
End With
ActiveSheet.Columns.AutoFit
Set Recordset = Nothing
Connection.Close
Set Connection = Nothing
End Sub
An easy way to get data in Excel, especially from Access, is to use the menu "Data > Access". This creates a connection to a table, that you can freely edit.
At the very least, that is a convenient way to limit your investigations to:
the query you typed (the connection string will always be OK, so if you're getting no values, it comes from the query)
or the VBA itself (if the table is returning values but not the corresponding VBA Sub, then you know it comes from the VBA itself, not the SQL).
I'm skipping the creation of connection becuse it's really straightforward; it's better to focus on what you can do once the table has been created.
Edit the connection
When you select the table and go to menu "Data > Properties", then in the window you click on the top right button "Connection properties", you get to the definition of the connection, i.e. some properties in the first tab and the actual definition in the second tab.
If you move the .mdb file, you'll have to change the connection string accordingly. There should be no other events forcing you to alter it.
If you want to type an actual complex query, you'll need to:
Change the command type from "Table" to "SQL"
Type the query in the bottom edit box.
Note if you want to define dynamic parameters in the WHERE clause, you can put question marks (?) instead of hardcoded values. Question marks can be linked to either constants (with a prompt to change their values) or cell.
Use in VBA
Once you checked with the connection that everything works, you have 2 solutions to put that in VBA.
Either use exactly the code you have above; in that case, you can make things easy by simply copying the connection string and the query.
Alternatively and this is what I would recommend, the table we have built previously can be updated very easily in VBA.
Use this piece of code:
WorksheetWithTable.ListObjects(1).QueryTable.Refresh
You really don't need more than this 1 line of code to do the refresh.
If you set your query to automatically refresh when a cell's value is being modified, then you do not even need it at all.
Note #1: Instead of an index in .ListObjects(1), you can use the table name.
Node #2: Refresh has an optional parameters to drive if the query is to be refresh in the background. True means the VBA code will not wait for the execution to end before moving to the next instruction. False, obviously, is the opposite.
The posted code is missing End If line. Perhaps this is just a posting typo because code should not compile and run.
The query SQL needs # delimiters for the date parameters:
Source = "SELECT * FROM Tracking WHERE [Date_Logged] BETWEEN #" & startdt & "# AND #" & stopdt & "# ORDER BY [Date_Logged]"
Text field would need apostrophe delimiters. Number field does not need delimiters.
I solved the answer to my own question after hours, i found a different set of code that worked fine. Thank you all for your help!
Sub getdatamdb()
Dim cn As Object, rs As Object
Dim intColIndex As Integer
Dim DBFullName As String
Dim TargetRange As Range
10 DBFullName = "X:\MyDocuments\CMS\CMS Database.mdb"
20 On Error GoTo Whoa
30 Application.ScreenUpdating = False
40 Set TargetRange = Sheets("Sheet1").Range("A1")
50 Set cn = CreateObject("ADODB.Connection")
60 cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & DBFullName & ";"
70 Set rs = CreateObject("ADODB.Recordset")
80 rs.Open "SELECT * FROM Tracking WHERE [Date_Logged] BETWEEN #" & startdt & "# AND #" & stopdt & "# ORDER BY [Date_Logged]", cn, , , adCmdText
' Write the field names
90 For intColIndex = 0 To rs.Fields.Count - 1
100 TargetRange.Offset(1, intColIndex).Value = rs.Fields(intColIndex).Name
110 Next
' Write recordset
120 TargetRange.Offset(1, 0).CopyFromRecordset rs
LetsContinue:
130 Application.ScreenUpdating = True
140 On Error Resume Next
150 rs.Close
160 Set rs = Nothing
170 cn.Close
180 Set cn = Nothing
190 On Error GoTo 0
200 Exit Sub
Whoa:
210 MsgBox "Error Description :" & Err.Description & vbCrLf & _
"Error at line :" & Erl & vbCrLf & _
"Error Number :" & Err.Number
220 Resume LetsContinue
End If
End Sub
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.
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.