I am trying to make an application that has the feature to import data from access into excel. I am starting with a table named ""1301 Array"" before I give user control over which table. The problem is I get the error "Cannot modify table structure. Another user has the table open", assuming because of the excel sheet I am writing in. Is there a work around to use TransferSpreadsheet for this?
Sub Importfromaccess()
Dim accappl As Access.Application
Dim strpathdb As String
Dim strpathxls As String
strpathdb = Application.GetOpenFilename("Access DataBase (*.accdb),*.accdb")
strpathxls = ActiveWorkbook.FullName
Set accappl = New Access.Application
accappl.OpenCurrentDatabase strpathdb
Dim Page As Worksheet
Dim lRow As Long, LCol As Long
Dim fullrange As String
Dim PageName As Variant
accappl.DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "1301 Array", strpathxls, True
accappl.Quit
End Sub
The solutions I have found online mostly use sql, but I have no idea how to write in that, or how they got sql to work in excel vba. The solution below seems to do something similar to what I need, but I'm not sure how to tweak to import a table into a new sheet and give it the same name.
Sub Workbook_Open()
Dim cn As Object, rs As Object
Dim intColIndex As Integer
Dim DBFullName As String
Dim TargetRange As Range
DBFullName = "D:\Tool_Database\Tool_Database.mdb"
Application.ScreenUpdating = False
Set TargetRange = Sheets("Sheet1").Range("A1") '1301 Array after creating it?
Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & DBFullName & ";"
Set rs = CreateObject("ADODB.Recordset")
rs.Open "SELECT * FROM ToolNames WHERE Item = 'Tool'", cn, , , adCmdText
' Write the field names
For intColIndex = 0 To rs.Fields.Count - 1
TargetRange.Offset(1, intColIndex).Value = rs.Fields(intColIndex).Name
Next
' Write recordset
TargetRange.Offset(1, 0).CopyFromRecordset rs
End Sub
Update: Am going to try and use this method
Sub AccessToExcel()
'Declare variables.
Dim dbConnection As ADODB.Connection
Dim dbRecordset As ADODB.Recordset
Dim dbFileName As String
Dim strSQL As String
Dim DestinationSheet As Worksheet
'Set the assignments to the Object variables.
Set dbConnection = New ADODB.Connection
Set dbRecordset = New ADODB.Recordset
Set DestinationSheet = Worksheets("Sheet2")
'Define the Access database path and name.
dbFileName = "C:\YourFilePath\Database1.accdb"
'Define the Provider for post-2007 database files.
dbConnection.Provider = "Microsoft.ACE.OLEDB.12.0;Data Source=" _
& dbFileName & ";Persist Security Info=False;"
'Use SQL's SELECT and FROM statements for importing Table1.
strSQL = "SELECT Table1.* FROM Table1;"
'Clear the destination worksheet.
DestinationSheet.Cells.Clear
With dbConnection
'Open the connection.
.Open
'The purpose of this line is to disconnect the recordset.
.CursorLocation = adUseClient
End With
With dbRecordset
'Create the recordset.
.Open strSQL, dbConnection
'Disconnect the recordset.
Set .ActiveConnection = Nothing
End With
'Copy the Table1 recordset to Sheet2 starting in cell A2.
'Row 1 contains headers that will be populated at the next step.
DestinationSheet.Range("A2").CopyFromRecordset dbRecordset
'Reinstate field headers (assumes a 4-column table).
'Note that the ID field will also transfer into column A,
'so you can optionally delete column A.
DestinationSheet.Range("A1:E1").Value = _
Array("ID", "Header1", "Header2", "Header3", "Header4")
'Close the recordset.
dbRecordset.Close
'Close the connection.
dbConnection.Close
'Release Object variable memory.
Set dbRecordset = Nothing
Set dbConnection = Nothing
Set DestinationSheet = Nothing
End Sub
The first version won't work because you are attempting to write to the Excel file that you currently have open.
Changing to the following line (of the 2nd code) will copy the data to another worksheet:
Set TargetRange = Sheets("WhateverName").Range("A1") 'or
Set TargetRange = Sheets(2).Range("A1")
'..if you know it is the 2nd sheet that
'you want to copy to. Then,
Worksheets(2).Name = "1301 Array"
You could, alternatively, create a new sheet:
Dim wsData As Worksheet
Set wsData = Worksheets.Add
wsData.Name = "1301 Array"
Set TargetRange = wsData.Range("A1")
Related
I am fairly new to Access and I have been trying for a while to run an Access query and paste the results in Excel using VBA. I have combined some code I found and I think I almost have it but cannot figure out the last step. Here is the code:
Sub test()
Dim ws As Worksheet
Dim A As Object
Dim rs As Object
Application.DisplayAlerts = False
Set A = CreateObject("Access.Application")
Set ws = ThisWorkbook.Sheets("Sheet1")
A.Visible = True
A.OpenCurrentDatabase ("access database path")
A.DoCmd.OpenQuery ("query name")
Set rs = A.CurrentDb().QueryDefs("query name").OpenRecordset()
If Not rs.EOF Then
ws.Range("A1").CopyFromRecordset rs
End If
rs.Close
Application.DisplayAlerts = True
End Sub
I am trying to run the query and paste the results in cell A1 in sheet 1.
I get a "run time error 3219" for the line:
Set rs = A.CurrentDb().QueryDefs("query name").OpenRecordset()
Any help would be greatly appreciated.
Thanks,
G
I adapted your code to fetch data from an Access query without needing to create a full Access.Application instance. Tested and working in Excel 2010.
Const cstrPath As String = "C:\share\Access\Database2.accdb"
Const cstrQuery As String = "qryBase"
Dim dbe As Object 'DAO.DBEngine '
Dim rs As Object 'DAO.Recordset '
Dim ws As Worksheet
Application.DisplayAlerts = True 'leave alerts on during testing '
Set dbe = CreateObject("DAO.DBEngine.120")
Set rs = dbe.OpenDatabase(cstrPath).OpenRecordset(cstrQuery)
If Not rs.EOF Then
Set ws = ThisWorkbook.Sheets("Sheet1")
ws.Range("A1").CopyFromRecordset rs
End If
rs.Close
Application.DisplayAlerts = True
I would use ADODB recordset. Try the below code. Here I'm connecting to an excel workbook, but you can use the same logic for access database, you just need to change the connection string.
Private con As ADODB.Connection
Private ra As ADODB.Recordset
' SqlString = SQL Query
' Sht = Sheet Name, where the output needs to be displayed
' Rng = Range ("C5"), where the output needs to be displayed
Sub DoSql(SqlString As String, Sht As String, Rng As String, Optional IncludeHeading As Boolean = False)
Dim a As String
Dim res As Variant
Set con = New ADODB.Connection
Set ra = New ADODB.Recordset
res = ""
'a = Set the appropriate connection string for your database
'The below connection is referring to the same excel workbook which contains the macro
a = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=""" & ThisWorkbook.FullName & """;Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
'MsgBox a
'MsgBox SqlString
If Not Left("" & con, 8) = "Provider" Then
con.Open a
End If
If Not ra.State = 0 Then
ra.Close
End If
ra.Open SqlString, con
If Not (ra.EOF And ra.BOF) Then
ra.MoveFirst
Sheets(Sht).Select
If IncludeHeading = True Then
For intColIndex = 0 To ra.Fields.Count - 1
Range(Rng).Offset(0, intColIndex).Value = ra.Fields(intColIndex).Name
Next
Range(Rng).Offset(1, 0).CopyFromRecordset ra
Else
Range(Rng).CopyFromRecordset ra
End If
End If
ra.Close
con.Close
End Sub
I I'm trying to perform a loop over a variable range of cells.
A query is then run with varibles relating to the cell text.
It appears to be looping through the cells but the error lies within sql because the object is already open. I have tried to close all connections before hand but get the same error as it moves to the next cell.
Dim cell as range
dim rng as range
set rng = range("D9:D" & ActiveSheet.Cells(ActiveSheet.Rows.Count, 1).End(xlUp).Row
For each cell in rng.Cells
cell.activate
Dim CELL2 as Variant
CELL2 = Activecell
dim cnn as new adodb.connection
dim rst as new adodb.recordset
dim ConnectionString as string
dim StrQuery1 as string
dim PathID as variant
ConnectionString = ' I have inserted relevant data here'
dim xConnect as object
for each xConnect in ActiveWorkbook.Connections
xConnect.delete
Next xConnect
cnn.Open ConnectionString
cnn.CommanTimeout = 900
StrQuery1 = "Declare #DocID int Select #DocId = DocumentID from Documents where Left(Filename,10) = '" & CELL2 "'Right(Filename,6) = 'SLDDRW' Declare #PrjId int Select #PrjId = ProjectID from DocumentsinProjects where DocumentId = #DocId Select Path from Projects where ProjectID = #PrjId
rst.open StrQuery1, cnn
PathID = rst!Path
Msgbox (CELL2)
Msgbox (PathId)
dim xConnect as object
for each xConnect in ActiveWorkbook.Connections
xConnect.delete
Next xConnect
Next Cell
I would suggest using the Close method for the connection. You might also want to look into opening the connection once at the beginning, sending multiple sql commands, and closing it at the end. A quick examples is included below
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.RecordSet
Dim ConnectionString As String
cnn.Open ConnectionString
For .....
Set rst = cnn.Execute( Insert SQL String here )
do things
Next .....
rst.Close
cnn.Close
Edit: After Dan's comment, here is another way that will open and close the entire connection every time
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.RecordSet
Dim ConnectionString As String
For .....
cnn.Open ConnectionString
Set rst = cnn.Execute( Insert SQL String here )
do things
rst.Close
cnn.Close
Next .....
Hopefully the question I'm asking is clear and to be honest I'm also new to using Microsoft VBA (literally started trying to use it today). I'm trying to "grab" a query/data table from a Microsoft Access Database and I'm having difficulty understanding the syntax and exactly what commands do. At the moment it appears I'm getting into the query, but only returning the very first cell of the data table with the code:
Private Sub Select_From_Access()
Dim cn As Object
Dim rs As Object
Dim strSql As String
Dim strConnection As String
Dim placementRange As Range
'DescriptionErrorByLot is the worksheet I want to put the table in, the range A1:Z44 is what would hypothetically be cleared
'if it needed to be once there is data there and needs to be updated
Worksheets("DescriptionErrorByLot").Range("A1:Z44").ClearContents
Set cn = CreateObject("ADODB.Connection")
'This is where I want the query (table) to be placed?
Set placementRange = Worksheets("DescriptionErrorByLot").Range("A1")
'Connection string containing provider and file path to the database
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Users\sjevne\Desktop\Database.accdb"
'Selecting the whole table from the query "jc_C2ComplaintCountbyLot10"? This is the queries name in the database
'To better explain what I'm talking about, there's buttons I can click on in the access database inside of the
'Reports section (click 'Reports' button) and then I click another button "Description errors by lot" and then
'A table/query with the name jc_C2ComplaintCountbyLot10 is open
strSql = "SELECT * FROM jc_C2ComplaintCountByLot10;"
cn.Open strConnection
Set rs = cn.Execute(strSql)
placementRange.CopyFromRecordset rs
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
Basically all I'm wondering right now is, what's actually allowing me to return the first cell in the data table I'm interested in? and how can I extend the range to grab the whole thing, obviously?
Any help would be much appreciated! Thanks in advance.
EDIT 1 : Code
EDIT 2 : I've been playing around with the code again, when I tried changing the SELECT * FROM to an existing connection that I was previously using to the database query that I want, and it said something about multi-valued cells and not being able to get data from a different database. (This connection to the worksheet was previously used to make an automated table, so as database table values changed, the spreadsheet did. All I'm trying to do now is just 'copy and paste' using a macro since the previous method is no longer available, sadly.
Here is something similar grabbing the whole table, I do not bring in the field names they are already there. Don't forget when you do this the IDs are coming with the table field data.
Private Sub getDataTable_Click()
Dim conn As Object ' connection
Dim rs As Object 'record set
Dim strSql As String
Dim strConnection As String
Dim placementRange As Range
'UPDATE THIS FOR YOUR WORKSHEET AND RANGE IF YOU WANT TO CLEAR BEFORE COPY
Worksheets("mtrInteraction").Range("I2:P25").ClearContents
Set conn = CreateObject("ADODB.Connection")
'update this for the workbook,worksheet, and range where you want it
'UPDATE THIS FOR THE SHEET AND THE RANGE WHERE YOU WANT THE TABLE, UPPER LEFT CORNER
Set placementRange = Worksheets("mtrInteraction").Range("I2")
'UPDATE THIS FOR YOUR PATH AND DB NAME
'Build your connection and path
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\yourpath\yourpath\updatethis.accdb"
'UPDATE THIS FOR YOUR QUERY, TABLE NAME IS ALL YOU HAVE TO CHANGE TO COPY THE WHOLE TABLE, THE DB NAME IS SPECIFIED IN THE CONNECTION ABOVE
'Make your sql query to select all from YOUR table name
strSql = "SELECT * FROM tbl_MTR;"
'open it you might want an error handler here
conn.Open strConnection
'get the recordset
Set rs = conn.Execute(strSql)
'copy your recordset in
placementRange.CopyFromRecordset rs
rs.Close
Set rs = Nothing
conn.Close
Set conn = Nothing
End Sub
Here is the same thing in a stub that has the field name you want and a data value in a cell for specific targeting:
Private Sub CommandButton1_Click()
Dim inputSheet As Worksheet
Dim fieldSTR As String
Dim placementRange As Range
Dim rs As Object 'record set
Dim conn As Object
Dim strQuery As String
Dim myDB As String
Set inputSheet = ThisWorkbook.Worksheets("Sheet1")
Set placementRange = inputSheet.Range("E2")
fieldSTR = CStr(inputSheet.Cells(3, 3).Value) 'C3 cell
myDB = "C:\yourpath\yourpath\updatethis.accdb"
Set conn = CreateObject("ADODB.Connection")
With conn
.Provider = "Microsoft.ACE.OLEDB.12.0" 'For *.ACCDB Databases
.ConnectionString = myDB
.Open
End With
strQuery = "SELECT * FROM " & _
"tbl_test WHERE Color = " & "'" & fieldSTR & "'" & ";"
'The below gives the same result as * but you could limit the fields returned as well
'tbl_test.ID, tbl_test.Color, tbl_test.number
MsgBox (strQuery)
Set rs = conn.Execute(strQuery)
placementRange.CopyFromRecordset rs
rs.Close
Set rs = Nothing
conn.Close
Set conn = Nothing
End Sub
This code works for me, it's stripped bare so it may not compile cleanly:
Sub LoadRecordset(Sheet1 As Worksheet, query As String)
Dim cnpubs As ADODB.Connection
Dim rsPubs As ADODB.Recordset
Set cnpubs = New ADODB.Connection
cnpubs.ConnectionString = "Driver={SQL Server};Server=TESTDS;Database=TEST1;UID=sa;PWD=WERQEWDS"
cnpubs.Open
Set rsPubs = New ADODB.Recordset
With rsPubs
.ActiveConnection = cnpubs
.Open query
fldCount = .Fields.Count
iRow = 1
Sheet1.Rows(iRow & ":" & Rows.Count).Delete
For iCol = 0 To fldCount - 1
Sheet1.Cells(iRow, iCol + 1).Value = .Fields(iCol).Name
Next
iRow = iRow + 1
Sheet1.Range("A" & iRow).CopyFromRecordset rsPubs
.Close
End With
cnpubs.Close
Set rsPubs = Nothing
Set cnpubs = Nothing
Sheet1.Cells.EntireColumn.AutoFit
End Sub
I'm trying to query a MS Access (2007-2010) database using Excel and print the query to my spreadsheet.
The below code prints only the field header into a specified cell and none of the other data in the selected field. Where am I going wrong? hints etc welcome.
Option Explicit
' Add reference to Microsoft ActiveX Data Objects Lib
Public Sub main(): On Error GoTo Err_handler
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
'open DB connection
cn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Databaselocation
cn.Open
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
'Query Stuff
rs.ActiveConnection = cn
rs.Open "SQL Query here;"
'does something
Dim fld As ADODB.Field
Dim rng As Range
Set rng = [a2]
For Each fld In rs.Fields
rng.Value = fld.Name
Set rng = rng.Offset(0, 2)
Next fld
Set rng = rng.Offset(2, -rs.Fields.Count)
rng.CopyFromRecordset rs
' closes db connection
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
Exit Sub
Err_handler:
MsgBox Err.Description
End Sub
You want to skip over one column at a time when displaying column names, not two, because the rng.CopyFromRecordset method will output the recordset with no gaps.
Set rng = rng.Offset(0, 1)
I tested your code and it does work with that change.
You must loop also in Rows in your rs (ADODB.Recordset) object. Use MoveNext method to move to next row.
I am currently looking for an alternative to the solution below, but using the ADO functionality so that the source workbook isn't opened. I am imagining this will decrease processing time?
Your thoughts..
Thanks
Sub CopyFilteredValuesToActiveWorkbook()
Dim wbSource As Workbook, wbDest As Workbook
Dim wsSource As Worksheet, wsDest As Worksheet
Dim rngSource As Range, rngDest As Range
Set wbSource = Workbooks.Open("\\Linkstation\rrm\X_DO_NOT_TOUCH_CC\MasterLogFile\Masterlogfile.xlsx", , True) 'Readonly = True
Set wsSource = wbSource.Worksheets("LogData")
wsSource.Range("$A$1:$H$3").AutoFilter Field:=3, Criteria1:="Opera"
Set rngSource = wsSource.Range("A:Z")
Set wbDest = ThisWorkbook
Set wsDest = wbDest.Worksheets("MLF")
Set rngDest = wsDest.Range("A:Z")
rngDest.Value = rngSource.Value 'Copies values over only, if you need formatting etc we'll need to use something else
wbSource.Close (False) 'Close without saving changes
End Sub
You could use a reference to Active X Data Objects 6.0, to use SQL queries
Const adOpenStatic = 3
Const adLockOptimistic = 3
Const adCmdText = &H1
Public Sub GetValues (path as String, destination as Range)
Dim conStr as String, strSQL as string
Dim con as new ADODB.Connection, rs as new ADODB.Recordset
conStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" & path & "';" & _
"Extended Properties=""Excel 12.0;HDR=YES;IMEX=1;"";"
strSQL = "SELECT * FROM [LogData$] WHERE [CriteriaColumn] = 'Opera'"
con.Open conStr
rs.open strSQL, con, adOpenStatic, adLockOptimistic, adCmdText
destination.CopyFromRecordset rs
rs.close
con.close
End Sub
Where the CriteriaColumn is the Header of the Column used as criteria to filter
You can call the subroutine as follows:
Dim path as string, rngDest as Range
path = "\\Linkstation\rrm\X_DO_NOT_TOUCH_CC\MasterLogFile\Masterlogfile.xlsx"
'The Upper left cell of the range that will receive the data:
Set rngDest = ThisWorkbook.Worksheets("MLF").Range("A1")
GetValues path, rngDest
You are missing this line:
Set rs = CreateObject("ADODB.Recordset")
For some reason Win XP will not run without it. It should be placed right after con.Open conStr.