How to run access query using excel VBA? - vba

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

Related

Error 3011 when doing transferspreadsheet

Running VBA through Access.
Attempting to transfer select queries from access to excel.
If I run all the code together, then I get 3011 run-time error on the 2nd DoCmd.TransferSpreadsheet.
If I comment out all the code related to PATH1, then the 2nd DoCmd.TransferSpreadhseet runs fine.
The Microsoft Access database engine could not find the object 'TabUSR1'. Make sure the object exists and that you spell its name and the path name correctly...
I have removed a good bit of code that I feel to be irrelevant to my issue. That is why there are so many variables you do not see any code for.
Dim tempR1 As String
Dim tempR2 As String
Dim tempValue1 As String
Dim tempValue2 As String
Dim tempValue3 As String
Dim tempValue4 As String
Dim tempValue5 As String
Dim dt As Date
Dim d As String
Dim row As String
Dim rngC As Range
Dim rngU As Range
Dim fpath As String
Dim strFileExists
Dim xlappC As Excel.Application
Dim xlbookC As Excel.Workbook
Dim xlsheetC As Excel.Worksheet
Dim xlappU As Excel.Application
Dim xlbookU As Excel.Workbook
Dim xlsheetU As Excel.Worksheet
fpath = "PATH1"
strFileExists = Dir(fpath)
If strFileExists <> "" Then
'set variables for Excel
Set xlappC = CreateObject("Excel.Application")
Set xlbookC = xlappC.Workbooks.Open(fpath)
Set xlsheetC = xlbookC.Worksheets("Audit Fees Remittance")
With xlappC
.Visible = False
.DisplayAlerts = False
.Workbooks.Open fpath
'Update Raw Data Cad and CSCT tab
Set xlsheetC = xlbookC.Worksheets("Raw Data CAD and CSCT")
With xlsheetC
Set rst = CurrentDb.OpenRecordset("Weekly CAN 5 Raw Data to include csct")
If rst.RecordCount > 0 Then
tempR2 = rst.RecordCount + 1
tempR2 = .Cells(.Rows.Count, "CV").End(xlUp).Offset(tempR2).Address(False, False)
tempR1 = .Cells(.Rows.Count, "A").End(xlUp).Offset(1).Address(False, False)
Set rngC = .Range(tempR1, tempR2)
rngC.Name = "TabFA8"
DoCmd.TransferSpreadsheet acExport, 10, "PATH1", True, "TabFA8"
.Rows(2).EntireRow.Delete
rst.Close
Set rst = Nothing
Else
rst.Close
Set rst = Nothing
End If
tempValue2 = "$A$2:" & tempR2
.Range(tempValue2).EntireColumn.AutoFit
tempR1 = ""
tempR2 = ""
End With
'Remit for US
fpath = "PATH2"
strFileExists = Dir(fpath)
If strFileExists <> "" Then
'set variables for Excel
Set xlappU = CreateObject("Excel.Application")
Set xlbookU = xlappU.Workbooks.Open(fpath)
Set xlsheetU = xlbookU.Worksheets("Remittance Tab")
With xlappU
.Visible = False
.DisplayAlerts = False
.Workbooks.Open fpath
'Update INTL Remittance tab
Set xlsheetU = xlbookU.Worksheets("INTL Remittance")
With xlsheetU
Set rst = CurrentDb.OpenRecordset("Weekly US 5 Remittance Tab B DHLG and Jas")
If rst.RecordCount > 0 Then
tempR2 = rst.RecordCount + 1
tempR2 = .Cells(.Rows.Count, "V").End(xlUp).Offset(tempR2).Address(False, False)
tempR1 = .Cells(.Rows.Count, "A").End(xlUp).Offset(1).Address(False, False)
If Len(tempR1) = 3 Then
row = Right(tempR1, 2)
Else
row = Right(tempR1, 3)
End If
'set range for renaming
'this will allow TransferSpreadhseet to know where to export to on the sheet
Set rngU = .Range(tempR1, tempR2)
rngU.Name = "TabUSR2"
DoCmd.TransferSpreadsheet acExport, 10, "Weekly US 5 Remittance Tab B DHLG and Jas", "PATH2", True, "TabUSR2"
'delete row with headers
.Rows(row).EntireRow.Delete
rst.Close
Set rst = Nothing
Else
rst.Close
Set rst = Nothing
End If
End With
While I cannot exactly understand or diagnose your issue, for maintenance and readability, consider separating all Excel and Access processes. Avoid walking over same opened files with both object libraries. Therefore, consider Excel's Range.CopyFromRecordset over Access's DoCmd.TransferSpreadsheet using the very recordset you create.
...
Set rst = CurrentDb.OpenRecordset("Weekly CAN 5 Raw Data to include csct")
...
Set rngC = .Range(tempR1, tempR2)
rngC.Name = "TabFA8"
rngC.CopyFromRecordset rst
rst.Close
...
Set rst = CurrentDb.OpenRecordset("Weekly US 5 Remittance Tab B DHLG and Jas")
...
Set rngU = .Range(tempR1, tempR2)
rngU.Name = "TabUSR2"
rngU.CopyFromRecordset rst
rst.Close
Parfait's suggestion of closing the workbook then doing the TransferSpreadsheet solved my issue.

VBS: Pull SQL to Excel File

I'm trying to create a simple VBS script that will allow a sql query to create an excel file. All of my code below works, except it puts all 3 columns I am selecting into cell A1. How can I get it to create a standard Excel output with the headers?
set objExcel = CreateObject("Excel.Application")
objExcel.Application.DisplayAlerts = False
set objWorkbook=objExcel.workbooks.add()
Dim Connection
Dim Recordset
Dim SQL
SQL = "SELECT PersonID, FirstName, LastName FROM [TestDB].[dbo].[Persons]"
Set Connection = CreateObject("ADODB.Connection")
Set Recordset = CreateObject("ADODB.Recordset")
Connection.Open = "Provider=SQLOLEDB; Data Source=compname\SQL; Initial Catalog=DB; UID=usera; Integrated Security=SSPI"
Recordset.Open SQL, Connection
dim resultString, oRS
Set oRS = Connection.Execute(SQL)
resultString = oRS.GetString
Recordset.Close
Set Recordset=nothing
Connection.Close
Set Connection=nothing
Set objWorksheet = objExcel.Worksheets("Sheet1")
objWorksheet.Name = "Third"
objWorksheet.Activate
objWorksheet.Cells(1, 1).Value = resultString
objWorkbook.Saveas "C:\Users\usera\Desktop\Testing.xlsx"
objWorkbook.Close
objExcel.workbooks.close
objExcel.quit
set objExcel = nothing
msgbox("Saved")
You're putting all of your data into a single string with resultString = oRS.GetString. Why would you expect that string to parse itself back out into individual values to populate cells?
You need to read from the recordset's Fields collection and populate each cell. Here's a sample that shows how you would do so. (Hint: your code contains lots of repetitive stuff that isn't necessary - pay attention to what mine is doing instead).
Dim Conn
Dim RS
Dim SQL
SQL = "SELECT PersonID, FirstName, LastName FROM [TestDB].[dbo].[Persons]"
Set Conn = CreateObject("ADODB.Connection")
Conn.Open = "Provider=SQLOLEDB; Data Source=compname\SQL; Initial Catalog=DB; UID=usera; Integrated Security=SSPI"
Set RS = Conn.Execute(SQL)
Set Sheet = ActiveSheet
Sheet.Activate
Dim R
R = 1
While RS.EOF = False
Sheet.Cells(R, 1).Value = RS.Fields(0)
Sheet.Cells(R, 2).Value = RS.Fields(1)
Sheet.Cells(R, 3).Value = RS.Fields(2)
RS.MoveNext
R = R + 1
Wend
RS.Close
Conn.Close

Transferring Data to Excel

I have the following code to transfer code to excel file:
Dim SaveAsStr As String
Dim appXL As Excel.Application
Dim wbk As Excel.Workbook
Dim wst As Excel.Worksheet
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim rs1 As ADODB.Recordset
Dim rs2 As ADODB.Recordset
Dim LR As Long
Dim startcell As Range
DoCmd.RunMacro "Guardarmcr"
Set appXL = CreateObject("Excel.Application")
appXL.Visible = True
Set wbk = appXL.Workbooks.Add
Set wst = wbk.Worksheets(1)
Set startcell = Range("D16")
Set cn = CurrentProject.AccessConnection
Set rs = New ADODB.Recordset
Set rs1 = New ADODB.Recordset
Set rs2 = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Source = "SELECT * FROM ExcelTitulotbl"
.Open
End With
With rs1
Set .ActiveConnection = cn
.Source = "SELECT * FROM Excelotptbl"
.Open
End With
With rs2
Set .ActiveConnection = cn
.Source = "SELECT * FROM ExcelEDTUDCtbl"
.Open
End With
With wst
'.QueryTables.Add Connection:=rs, Destination:=.Range("A1")
'.QueryTables(1).Refresh
.QueryTables.Add Connection:=rs1, Destination:=.Range("d16")
.QueryTables(1).Refresh
.Range("A16").EntireRow.Delete
.Range("e2").Font.Bold = True
.Range("e2").Font.Name = "Calibri"
.Range("e2").Font.Size = 14
.Range("e2") = "VALORACION"
.Range("D5") = "Descripción"
.Range("j5") = "Profesional Colaborador"
.Range("j6") = "Profesional Chilectra"
.Range("e5") = rs("proyectoMain")
.Range("k5") = rs("Empleado")
.Range("k6") = rs("chilectramain")
.Range("B15") = "Recargo"
.Range("D15") = "Número"
.Range("E15") = "Apdto"
.Range("F14") = "Tipo"
.Range("F15") = "Ocurrencia"
.Range("g15") = "Especialidad"
.Range("h14") = "Tipo"
.Range("h15") = "Activo"
TotalE
End With
wbk.Saved = True
Set wks = Nothing
Set wbk = Nothing
Set appXL = Nothing
End Sub
Most of it works all right but if I try to run the command a Second time the worksheet opens but the vba fails showing a 1004 error. It has something to do with LR = Range("E" & Rows.Count).End(xlUp).Row. If I quit the form and enter it again it works the first time but not a second.
Will appreciate some help on this, thanks.
Looks like you are calling the TotalE sub routine, but in it you are not telling the range explicitly what workbook to use.
Activeworkbook.Range() may be better or when you add the workbook, capture the name and pass it to the sub routine.
Strange, but your only calculating the last row in column E and putting that value + 2 into column D two rows down.
All you really need is this code to replace your call to TotalE:
ActiveWorkbook.Sheets(1).Range("D" & ActiveWorkbook.Sheets(1).Range("E" & Rows.Count).End(xlUp).Row + 2) = ActiveWorkbook.Sheets(1).Range("E" & Rows.Count).End(xlUp).Row + 2
End Sub

Importing data from access into an open excel spreadsheet? (Excel VBA)

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")

ADO functionality.. copy, filter, paste from closed workbook to active wrokbook

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.