Import MDB Table into EXCEL via vba - Need field names/headers - vba

I have this bit of code that I found online that will import access records into excel. strFilePath is the filepath for the MDB and strTableName is the table name I am looking to import
Sub importAccessdata()
Dim cnn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sQRY As String
Dim strFilePath As String
strFilePath = Sheets("Setup").Range("C2").Value
strTableName = Sheets("Setup").Range("C4").Value
Set cnn = New ADODB.Connection
Set rs = New ADODB.Recordset
cnn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & strFilePath & ";"
sQRY = "SELECT * FROM " & strTableName & ""
rs.CursorLocation = adUseClient
rs.Open sQRY, cnn, adOpenStatic, adLockReadOnly
Application.ScreenUpdating = False
Sheet3.Range("A1").CopyFromRecordset rs
rs.Close
Set rs = Nothing
cnn.Close
Set cnn = Nothing
Exit Sub
End Sub
All of the records import very fast however I cannot get the field names to populate with them! Is there a simple modification I can do to carry the field names with the data?

This is what I use-
Private Sub PullSummaryData()
Const strDb As String = "C:\db\AccessDatabase.accdb"
Dim rs As ADODB.Recordset
Dim cn As ADODB.Connection
Dim i As Integer
Sheets("Summary").Select
Const strQry As String = "SELECT * FROM [AccessDataTable]"
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strDb & ";"
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Open strQry
End With
With Sheets("Summary")
For i = 1 To rs.Fields.Count
.Cells(2,i).Value = rs.Fields(i-1).Name 'fields is a 0 based collection
Next i
.Range("A3").CopyFromRecordset rs
End With
rs.Close
cn.Close
End Sub

Related

ADODB EXCEL SQL CopyFromRecordset to slow

I use VBA and ADODB Connection in Excel to realize VLOOKUP with SQL in attached code:
The Line "ThisWorkbook.Worksheets("result").Range("A1").CopyFromRecordset rs" takes to much time. Is there a way to use "INSERT INTO" in "strSQL" and skip "copyfromrecordset" instead to speedup the makro?
Sub vlookup()
Dim cn As Object
Dim rs As Object
Dim strConnection As String
Dim strSQL As String
Worksheets("result").UsedRange.Clear
Set cn = CreateObject("ADODB.CONNECTION")
strConnection = "DRIVER={MICROSOFT EXCEL DRIVER (*.xls, *.xlsx, *.xlsm, *.xlsb)}; DBQ=" & ThisWorkbook.FullName
With cn
.Open strConnection
strSQL = "SELECT table1$.*, table2$.price FROM [table1$] LEFT JOIN [table2$] ON table1$.animal = table2$.animal"
Set rs = CreateObject("ADODB.RECORDSET")
With rs
.Source = strSQL
.ActiveConnection = strConnection
.CursorType = adOpenKeyset
.LockType = adLockOptimistic
.Open
ThisWorkbook.Worksheets("result").Range("A1").CopyFromRecordset rs
.Close
End With
End With
cn.Close
Set cn = Nothing
Set rs = Nothing
End Sub
Found this question while struggling with legacy code which had CopyFromRecordSet. In my experience, it is horribly slow for anything more than a trivial number of lines.
Try extracting the Recordset information into 2D Variant Array and set the Value property of the respective Excel Range. Chunk your data if you think memory is going to be a constraint. The solution is involved. You'll need to do the bounds checks and also transpose the data before setting the Value property. Full answer below (without chunking):
Sub CopyFromRecordset(rngStart As Range, rstData As Variant)
Dim buffer As Variant
Dim i As Long, j As Long
If InStr(TypeName(rstData), "RecordSet") > 0 Then
rstData.MoveLast
rstData.MoveFirst
buffer = rstData.GetRows(rstData.RecordCount)
ReDim buffer2(UBound(buffer, 2), UBound(buffer, 1)) As Variant
For i = 0 To UBound(buffer, 1)
For j = 0 To UBound(buffer, 2)
buffer2(j, i) = buffer(i, j)
Next j
Next i
rngStart.Application.Range(rngStart, rngStart.Offset(UBound(buffer2, 1), UBound(buffer2, 2))).Value = buffer2
Else
Debug.Print ("Expected RecordSet, found " & TypeName(rstData))
End If
End Sub
Sub vlookup()
Dim cn As Object
Dim rs As Object
Dim strConnection As String
Dim strSQL As String
Worksheets("result").UsedRange.Clear
Set cn = CreateObject("ADODB.CONNECTION")
strConnection = "DRIVER={MICROSOFT EXCEL DRIVER (*.xls, *.xlsx, *.xlsm, *.xlsb)}; DBQ=" & ThisWorkbook.FullName
With cn
.Open strConnection
strSQL = "SELECT table1$.*, table2$.price FROM [table1$] LEFT JOIN [table2$] ON table1$.animal = table2$.animal"
Set rs = CreateObject("ADODB.RECORDSET")
With rs
.Source = strSQL
.ActiveConnection = strConnection
.CursorType = adOpenKeyset
.LockType = adLockOptimistic
.Open
CopyFromRecordset ThisWorkbook.Worksheets("result").Range("A1"), rs
.Close
End With
End With
cn.Close
Set cn = Nothing
Set rs = Nothing
End Sub

SQL Query Returns empty Recordset

I'm trying to learn how to connect to a SQL Server DB from Excel DB. I've tried to reduce the code to dead simple to begin with. I've looked at several answers to related questions, however, I cannot figure out why this doesn't work. It executes all the way through. (The code shown here is somewhat anonymized.)
The query finds the database, because if the table name is invalid it throws an error. However it always returns record count = -1. I can eyeball the table in MSSMS and it has data. Same result for other tables in the DB.
Public Sub ADOtest1()
Dim Conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strConnString As String
strConnString = "Provider='SQLOLEDB'" & ";" & _
"Data Source='XXX-XPS\SQLEXPRESS'" & ";" & _
"Initial Catalog='XXXXX'" & ";" & _
"Integrated Security='SSPI'"
Set Conn = New ADODB.Connection
Conn.Open strConnString
' the query finds the DB, because if the table name is incorrect, it throws an error
strSQLString = "SELECT * from t300_XXXX"
Set rs = Conn.Execute(strSQLString)
wrkRecordCount = rs.RecordCount
'--- just some test breakpoints
If wrkRecordCount = -1 Then
a = "" '--- code keeps arriving here
Else
a = ""
End If
rs.Close
Conn.Close
End Sub
Answer from Srinika below worked:
Set rs = Conn.Execute(strSQLString)
rs.Close
rs.CursorLocation = adUseClient
rs.Open
I'll post two examples, so please refer.
First Example
Sub ExampleSQL()
Dim cnn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strSQL As String
Set cnn = New ADODB.Connection
'Set the provider property to the OLE DB Provider for ODBC.
'cnn.Provider = "MSDASQL"
'cnn.Provider = "Microsoft.ACE.OLEDB.12.0"
'cnn.Provider = "MSOLAP"
'cnn.Provider = "SQLOLEDB.1"
' Open a connection using an ODBC DSN.
cnn.ConnectionString = "driver={SQL Server};" & _
"server=severname;uid=sa;pwd=password;database=test"
Set rs = New ADODB.Recordset
strSQL = "SELECT * FROM [your Table] "
rs.Open strSQL, cnn.ConnectionString, adOpenForwardOnly, adLockReadOnly, adCmdText
cnn.Open
If cnn.State = adStateOpen Then
Else
MsgBox "Sever is not connected!! "
Exit Sub
End If
If Not rs.EOF Then
With Ws
.Range("a4").CurrentRegion.ClearContents
For i = 0 To rs.Fields.Count - 1
.Cells(4, i + 1).Value = rs.Fields(i).Name
Next
.Range("a5").CopyFromRecordset rs
.Columns.AutoFit
End With
Else
MsgBox "No Data!!", vbCritical
End If
rs.Close
Set rs = Nothing
cnn.Close
Set cnn = Nothing
End Sub
Second Example
Sub getDataFromServer()
Dim con As New ADODB.Connection
Dim cmd As New ADODB.Command
Dim rs As New ADODB.Recordset
Dim i As Integer
con.ConnectionString = "Provider=SQLOLEDB.1;" _
& "Server=(local);" _
& "Database=TEST;" _
& "Integrated Security=SSPI;" _
& "DataTypeCompatibility=80;"
con.Open
Set cmd.ActiveConnection = con
cmd.CommandText = "SELECT * FROM [your Table]"
Set rs = cmd.Execute
Range("A1").CopyFromRecordset rs
con.Close
Set con = Nothing
End Sub

Send a recordset to another macro

I have a macro for updating an SQL table in an Excel Add-in.
In order to use the same macro from multiple files I want to be able to create the recordset outside of the connection and then send it as a parameter to the update macro. Is this possible?
I have tried looking at the solutions found for in memory recordsets but these seemes to focus more on creating the columns rather than column-value pairs.
Sub test()
Dim ws As Worksheet
Dim serverName As String
Dim dataBase As String
Dim forecastDate As Date
Dim projectNum As Long
Dim SqlStr As String
Dim rst As New ADODB.Recordset
Set ws = ActiveSheet
serverName = "Servername"
dataBase = "database"
forecastDate = ws.Cells(2, "B").Value
projectNum = ws.Cells(3, "B").Value
SqlStr = "SELECT * From forecast WHERE forecastDate='" & forecastDate & "' AND projectNum = '" & projectNum & "';"
Set rst = New ADODB.Recordset
rst!forecastDate = forecastDate
rst!projectNum = projectNum
rst!Data = Cells(4, "B").Value
Application.Run "updateMacro", serverName, dataBase, SqlStr, rst
rst.Close
End Sub
'Part of the updateMacro:
Set conn = New ADODB.Connection
cs = "DRIVER=SQL Server;DATABASE=" & dataBase & ";SERVER=" & serverName & ";Trusted_connection=yes;"
conn.Open cs
'Set rst = New ADODB.Recordset
rst.Open SqlStr, conn, adOpenDynamic, adLockOptimistic 'adLockPessimistic
If rst.EOF Then
rst.AddNew
End If
'get the recordset from caller macro and update
rst.Update
rst.Close
Set rst = Nothing
conn.Close
Set conn = Nothing
I would like to create the recordset outside of the updateMacro and use it in that macro or create some sort of column-value pairs that could be copied to the recordset in the updateMacro.
You can declare the recordset as global or also pass the recordset between functions/subs. Please see code below for an example:
Option Explicit
'Global Recordset to be sued by other functions
Private rsMain As ADODB.Recordset
Public Function ImportData(ByVal fyYear As String) As Long
Dim sConnString As String, sqlYears As String
Dim conn As ADODB.Connection
Dim tCount As Long
sConnString = "Provider=SQLOLEDB;Data Source=server2;" & "Initial Catalog=FPSA;" & "Integrated Security=SSPI;"
sqlYears = "select ltrim(rtrim(FinYearDesc)) as FinYearDesc, Month, AccountType, ltrim(rtrim(AccountName))as AccountName, " & _
"ActualValue, BudgetValue from [GL_AccountMovements] where FinYearDesc >= '" & fyYear & "'"
Set conn = New ADODB.Connection
Set rsMain = New ADODB.Recordset
rsMain.CursorLocation = adUseClient
rsMain.Open sqlYears, conn, _
ADODB.adOpenForwardOnly, _
ADODB.adLockBatchOptimistic
Set rsMain.ActiveConnection = Nothing
conn.Close
If Not rsMain.EOF Then
tCount = rsMain.RecordCount
End If
ImportData = tCount
End Function
'An example of using Global Recordset
Function GetAccountsByYearMonth(ByVal strYTDLastYear as String) As Double
Dim lastYearYTDAct As Double
rsMain.Filter = strYTDLastYear
Do While Not rsMain.EOF
lastYearYTDAct = lastYearYTDAct + rsMain.Fields("ActualValue")
rsMain.MoveNext
Loop
GetAccountsByYearMonth = lastYearYTDAct
End Function
Thanks

"Object Required" error

I have an SQL query that I'm running out of Excel. The objective is to run the query and paste the data into a designated location:
Public Function Pull_SQL_Data()
''''On Error GoTo Err:
Worksheets("Data").Select
Range("B7").Select
Do Until ActiveCell = ""
ActiveCell.Offset(1).Select
Loop
Range("B:S", ActiveCell.Offset(-1, 3)).ClearContents
Worksheets("Data").Select
Range("B7").Select
Dim cnPubs As New ADODB.Connection
Dim strConn As String
Dim rstRecordsets As New ADODB.Recordset
Dim intColIndex As Integer
Dim strSQL As Variant
Application.ScreenUpdating = False
Application.Cursor = xlWait
Set cnPubs = New ADODB.Connection
Set rsPubs = New ADODB.Recordset
Set outCell = Sheets("Data").Range("B7")
strSQL = Sheets("SQL").Range("G1")
strConn = "PROVIDER=SQLOLEDB;"
cnPubs.CommandTimeout = 240
strConn = strConn & "DATA SOURCE=CFS-Serversql;INITIAL CATALOG=UserAnalysis;"
strConn = strConn & "INTEGRATED SECURITY=sspi;"
cnPubs.Open strConn
With rsPubs
.ActiveConnection = cnPubs
.Open strSQL, cnPubs, adOpenStatic, adLockReadOnly, adCmdText
Sheets("Data").Range("B7:S500").ClearContents
Sheets("Data").Range("B4").CopyFromRecordset rsPubs
End With
rsPubs.Close
cnPubs.Close
Set rsPubs = Nothing
Set cnPubs = Nothing
Application.Cursor = xlDefault
Exit Function
Err:
MsgBox "The following error has occured-" & vbCrLf & vbCrLf & VBA.Error, vbCritical, "SQL Connection"
MsgBox VBA.Err
Application.Cursor = xlDefault
Worksheets("DWH").Select
Range("A1").Select
End Function
When run I get:
The following error has occurred- Object required" Error code 424.
Why am I experiencing this issue?
Does this work?
Public Function Pull_SQL_Data()
Dim ws As Worksheet
Dim cnPubs As ADODB.Connection
Dim rsPubs As ADODB.Recordset
Dim strConn As String
Dim strSQL As Variant
Set ws = Worksheets("Data")
Set cnPubs = New ADODB.Connection
Set rsPubs = New ADODB.Recordset
strSQL = Sheets("SQL").Range("G1").Value
strConn = "PROVIDER=SQLOLEDB;DATA SOURCE=CFS-Serversql;" & _
"INITIAL CATALOG=UserAnalysis;INTEGRATED SECURITY=sspi;"
cnPubs.Open strConn
rsPubs.Open strSQL, cnPubs, adOpenStatic, adLockReadOnly, adCmdText
ws.Range("B7:S500").ClearContents
If Not rsPubs.EOF Then
ws.Range("B4").CopyFromRecordset rsPubs
Else
MsgBox "No records were returned!"
End If
rsPubs.Close
cnPubs.Close
End Function

Copying ADO recordset into excel worksheet

I'm trying to open a CSV file and query it and return the results into column A of the second worksheet of "ThisWorkbook".
I'm not getting any errors so I do not see why it is not copying the record set into excel.
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
Set rs = New ADODB.Recordset
Dim currentDataFilePath As String
Dim currentDataFileName As String
Dim nextRow As Integer
currentDataFilePath = "C:\Users\M\folder\"
currentDataFileName = "csv-file"
con.Open "Provider=Microsoft.JET.OLEDB.4.0;" & _
"Data Source=" & currentDataFilePath & ";" & _
"Extended Properties=""text;HDR=NO;FMT=Delimited;IMEX=1"""
'rs.ActiveConnection = con
rs.Open "SELECT Name FROM [" & currentDataFileName & ".csv] WHERE Datatype ='TYPE3'",
con
ThisWorkbook.Worksheets("Sheet2").Range("A:A").CopyFromRecordset rs
rs.Close
con.Close
Set rs = Nothing
Set con = Nothing
End Sub
You might refer to the CopyFromRecordset() method.
Based on your code above, after the rs.Open command you would add something like this:
ActiveWorksheet.Range("A1").CopyFromRecordset rs
See more here: http://msdn.microsoft.com/en-us/library/office/ff839240%28v=office.15%29.aspx