Import table from SQL to Access using Excel VBA - sql

I have an Excel tool for doing actuarial calculations on data from SQL. The tool imports the table from SQL to my Excel book then does some calculations on the data set.
I want to take the table from SQL (I use CopyFromRecordSet to paste into my spreadsheet) and rather insert that table into an Access db.
Dim acc As Object
Dim TblName As String, DBName As String, scn As String
Set acc = CreateObject("Access.Application")
Set rs = New ADODB.Recordset
scn = ThisWorkbook.Worksheets("AXIS Tables").Range("A3").Value
DBName = ThisWorkbook.Worksheets("AXIS Tables").Range("B3").Value
Call CreateConnectionSQL.CreateConnectionSQL
acc.OpenCurrentDatabase ActiveWorkbook.Path & "\" & scn & "\Input.accdb"
rs.ActiveConnection = cn
rs.CursorType = adOpenForwardOnly
rs.LockType = adLockReadOnly
rs.Source = "SELECT * FROM" DBName
rs.Open
TblName = "SAM"
Call DoCmd.TransferDatabase(TransferType:=acImport, _
databaseName:=rs, _
ObjectType:=acTable, _
Source:=rs.Fields, _
Destination:=acc)
rs.Close
Call CreateConnectionSQL.CloseConnectionACC
acc.CloseCurrentDatabase
acc.Quit
Set acc = Nothing
I tried a plethora of methods, I spent dozens of hours googling. I assume that RecordSet is a virtual database in Excel where the data is stored. I want to dump that data into a new table in Access.

Create a sheet called AXIS in your workbook to hold the query results before importing into Access.
Option Explicit
Sub CopyToAccess()
Const TABLENAME As String = "AXIS"
Const SHEETNAME As String = "AXIS" ' create this sheet
Const SQL As String = "SELECT * FROM TABLE1"
Dim acc As Object, cn As ADODB.Connection, rs As ADODB.Recordset
Dim rng As Range, ws As Worksheet
Dim sPath As String, sAddr As String, n As Long, i As Integer
Dim scn As String, dbname As String, dbpath As String
sPath = ThisWorkbook.Path
With ThisWorkbook.Worksheets("AXIS Tables")
scn = .Range("A3").Value
dbname = .Range("B3").Value
End With
dbpath = sPath & "\" & scn & "\" & dbname
' connect and query sql database
Set cn = CreateConnectionSQL
Set rs = New ADODB.Recordset
rs.ActiveConnection = cn
rs.CursorType = adOpenForwardOnly
rs.LockType = adLockReadOnly
rs.Source = SQL
rs.Open
' clear sheet
Set ws = ThisWorkbook.Worksheets(SHEETNAME)
ws.Cells.Clear
' set field names as header
For i = 1 To rs.Fields.Count
ws.Cells(1, i) = rs(i - 1).Name
Next
' copy record set to sheet
ws.Range("A2").CopyFromRecordset rs
Set rng = ws.Range("A1").CurrentRegion
n = rng.Rows.Count - 1
sAddr = ws.Name & "!" & rng.AddressLocal
sAddr = Replace(sAddr, "$", "") ' remove $ from address
MsgBox n & " records imported to " & sAddr, vbInformation
cn.Close
' open ACCESS
Set acc = CreateObject("Access.Application")
acc.OpenCurrentDatabase dbpath
' clear out any existing table
On Error Resume Next
acc.DoCmd.DeleteObject acTable, TABLENAME
On Error GoTo 0
' export sheet into access
acc.DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel9, TABLENAME, _
sPath & "/" & ThisWorkbook.Name, True, sAddr
' finish
acc.CloseCurrentDatabase
acc.Quit
Set acc = Nothing
MsgBox "Export to " & dbpath & " table " & TABLENAME & " complete", vbInformation
End Sub
Function CreateConnectionSQL() As ADODB.Connection
Const SERVER As String = "server"
Const DB As String = "database"
Const UID As String = "user"
Const PWD As String = "password"
Dim sConStr As String
sConStr = "Driver={SQL Server Native Client 11.0};Server=" & SERVER & _
";Database=" & DB & ";Uid=" & UID & ";Pwd=" & PWD & ";"
'Debug.Print sConStr
Set CreateConnectionSQL = CreateObject("ADODB.Connection")
CreateConnectionSQL.Open sConStr
End Function

Related

How to bring a recordset into a table in Access

an access newbie here. I am trying to write a VBA code to query from an SQL database, and append the values into an access table. For this, i wrote below code but so far, i could only write a query and create a connection to the server. But i don't know how to bring it into the access table. Can you help me with this?
Sub getInv()
Dim RowCount As Long, ColCount As Long
Dim cnn As Object
Dim RS As Object
Set cnn = CreateObject("ADODB.Connection")
Set RS = CreateObject("ADODB.Recordset")
Dim SQLQuery As String
SQLQuery = _
"SELECT " & _
"PSDDD.SDDPP, PSDDD.SPPRD " & _
"WHERE " & _
"PSDDD.SDDPP = '2244556'" & _
"ORDER BY " & _
"PSDDD.SDDPP ASC, PSDDD.SPPRD DESC "
ConnectString = _
"DRIVER={Client Access ODBC Driver (32-bit)};" & _
"UID=abbsx;PWD=password;" & _
"SYSTEM=ABCSQT;DBQ=SSTNCHP22DB;"
cnn.Open (ConnectString)
RS.Open SQLQuery, cnn
' I believe i should put the code for writing into access table here.
'Close the Recordset and Connection
RS.Close
cnn.Close
Set RS = Nothing
Set cnn = Nothing
Exit Sub
erden. I hope this code gives you inspiration to solve your problem.
Public Function appendSelectedStudentsIntoPoolTable(Interest As String) As Long
Dim rSQL As String, rParams As String
Dim aSQL As String, aParams As String
Dim sourceTable As String, targetTable As String
sourceTable = "tStudents"
targetTable = "tStudentsPool"
'Note for targetTable: ID column not set to autonumber because to preserve
'original data as in the source table. But you can use it as PK as long no
'duplication on IDs.
rParams = "PARAMETERS [par_interest] Text(50); "
rSQL = rParams & "SELECT ID, Email, FirstName " & _
"FROM " & sourceTable & _
" WHERE Interest = par_interest;"
aParams = "PARAMETERS [par_ID] Long, [par_Email] Text(255), " & _
"[par_FirstName] Text(50); "
aSQL = aParams & "INSERT INTO " & targetTable & _
" (ID, Email, FirstName) " & _
"VALUES (par_ID, par_Email, par_FirstName);"
Dim db As DAO.Database
Dim rQDf As DAO.QueryDef
Dim aQdf As DAO.QueryDef
Dim rs As DAO.Recordset
Dim rec As Variant
Dim rsCount As Long 'change data type as needed
Dim appendedCount As Long 'same as rsCount data type
Dim i As Long 'same as rsCount data type
'On Error GoTo commit_failed
Set db = CurrentDb
Set rQDf = db.CreateQueryDef("", rSQL)
rQDf.Parameters("par_interest") = Interest
Set rs = rQDf.OpenRecordset()
With rs
On Error Resume Next: .MoveLast
On Error Resume Next: .MoveFirst
If .RecordCount > 0 Then
Do While Not rs.EOF
Set aQdf = db.CreateQueryDef("", aSQL)
aQdf.Parameters("par_ID") = !ID
'add routine(s) to check existing ID on pool table here
'before record append to pool table
'to prevent duplicate ID. For now, i skip it.
aQdf.Parameters("par_Email") = !Email
aQdf.Parameters("par_FirstName") = !FirstName
aQdf.Execute dbFailOnError
aQdf.Close
appendedCount = appendedCount + 1
.MoveNext
Loop
.Close
rQDf.Close
End If
End With
appendSelectedStudentsIntoPoolTable = appendedCount: Exit Function
commit_failed:
appendSelectedStudentsIntoPoolTable = 0
'You can put error handler here
End Function

Automation Error with SQL Query with SUM and GROUP BY on an Excel Table

I am trying to group by and sum specific columns by using SQL queries and copy the result into another work sheet but it's throwing
Run-time error -2147217887 (80040e21): Automation Error
I can't figure out why for whatever reason and throwing error at .Open.
please find the pics of Excel table
Code:
Sub CreateConsolidatedTable()
Const adOpenKeyset = 1
Const adLockOptimistic = 3
Const WORKSHEETNAME As String = "Sheet1"
Const TABLENAME As String = "Table1"
Dim conn As Object, rs As Object
Dim tbl As ListObject
Dim Destination As Range
Set Destination = ThisWorkbook.Worksheets("Sheet2").Range("C1")
Set rg = ThisWorkbook.Worksheets("Sheet1").UsedRange
Set tbl = ThisWorkbook.Worksheets("Sheet1").ListObjects.Add(xlSrcRange, rg, , xlYes)
'Set tbl = Worksheets(WORKSHEETNAME).ListObjects(TABLENAME)
Set conn = CreateObject("ADODB.Connection")
conn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1"";"
conn.Open
' On Error GoTo CloseConnection
Set rs = CreateObject("ADODB.Recordset")
With rs
.ActiveConnection = conn
.CursorType = adOpenKeyset
.LockType = adLockOptimistic
.Source = getSQL(tbl)
.Open
With Destination
tbl.HeaderRowRange.Copy .Range("c1")
.Range("c2").CopyFromRecordset rs
.Parent.ListObjects.Add SourceType:=xlSrcRange, Source:=.Range("c1").CurrentRegion, XlListObjectHasHeaders:=xlYes, TableStyleName:=tbl.TableStyle
End With
End With
CloseRecordset:
rs.Close
Set rs = Nothing
CloseConnection:
conn.Close
Set conn = Nothing
End Sub
Function getSQL(tbl As ListObject) As String
Dim SQL As String, SheetName As String, RangeAddress As String
SQL = "SELECT DISTINCTROW [DATE_], [ACCOUNT_CODE], Sum([PRINCIPAL_DUE]) AS [Sum Of PRINCIPAL_DUE],[GL_HEAD_CODE_PRINCIPAL], Sum([INTEREST_DUE]) AS [INTEREST_DUE],[INTEREST_RATE]" & _
" FROM [SheetName$RangeAddress]" & _
" GROUP BY [ACCOUNT_CODE], [GL_HEAD_CODE_PRINCIPAL], [DATE_];"
SheetName = tbl.Parent.Name
RangeAddress = tbl.Range.Address(False, False)
Debug.Print SheetName
Debug.Print RangeAddress
SQL = Replace(SQL, "SheetName", SheetName)
SQL = Replace(SQL, "RangeAddress", RangeAddress)
getSQL = SQL
End Function
The following SQL line works fine for me, it groups and sums accordingly.
SQL = "SELECT [DATE_], [ACCOUNT_CODE], Sum([PRINCIPAL_DUE]) AS [Sum Of PRINCIPAL_DUE],[GL_HEAD_CODE_PRINCIPAL], Sum([INTEREST_DUE]) AS [INTEREST_DUE] " & _
" FROM [SheetName$RangeAddress]" & _
" GROUP BY [ACCOUNT_CODE], [GL_HEAD_CODE_PRINCIPAL], [DATE_];"
That's based on my mock-up data. If you're getting the same number of rows output as input, then check that the fields you're grouping by aren't hiding any extra data (like a time-stamp in the date column for instance) that would fragment the grouping.

How to set-up headers to a newly exported excel file by updating the below macro, which generates new excel file every time it was run?

The below query generates a new excel file with data from sql server every time it was run. But the generated excel file don't have column headers in it, making it difficult to understand which column is what. So I am interested inserting 5 bold column header names like column_header1, column_header2....column_header5 in the first row of the excel and let the data start from the second row
Sub TEXT()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strCon, strSQL As String
Dim MRC As Variant
strCon = "some_string_connection"
MRC = "" & Worksheets("SQL Text").Range("D4").Value & ""
strSQL = MRC 'Sql Query
Sheets("Summary").Select
With ActiveWorkbook.Connections("Connection1").OLEDBConnection
.CommandText = HR
.CommandType = xlCmdSql
End With
ActiveWorkbook.Connections("Connection1").Refresh
Folder = "U:\" 'Path in U drive
Filename = "Filename" & ".xls"
fpath = Folder & Filename
cn.Open strCon
cn.CommandTimeout = 0
rs.ActiveConnection = cn
rs.Open strSQL
Set fs = CreateObject("Scripting.FileSystemObject")
Set A = fs.CreateTextFile(fpath)
A.Write (rs.GetString(adClipString, , , vbCrLf, ""))
rs.Close
cn.Close
Set cn = Nothing
End Sub
You are writing to the Excel file here:
A.Write (rs.GetString(adClipString, , , vbCrLf, ""))
Thus, this is the place, where you should put your headers. Something like this should be ok:
A.Write "column_header1, column_header2, column_header5" & vbCrLf & _
rs.GetString(adClipString, , , vbCrLf, "")
There are two method.
First method.
Use rs.field and variant array.
Dim vR() As Variant
Dim str As String
For i = 0 To Rs.Fields.Count - 1
ReDim Preserve vR(i)
vR(i) = Rs.Fields(i).Name
Next
Second method is add new workbook and write fields and record.
If Not Rs.EOF Then
With Ws
.Range("a4").CurrentRegion.Clear
For i = 0 To Rs.Fields.Count - 1
.Cells(1, i + 1).Value = Rs.Fields(i).Name
Next
.Range("a2").CopyFromRecordset Rs
.Columns.AutoFit
End With
Else
MsgBox "There is no record!", vbCritical
End If
First full code.
Sub TEXT()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strCon, strSQL As String
Dim MRC As Variant
strCon = "some_string_connection"
MRC = "" & Worksheets("SQL Text").Range("D4").Value & ""
strSQL = MRC 'Sql Query
Sheets("Summary").Select
With ActiveWorkbook.Connections("Connection1").OLEDBConnection
.CommandText = HR
.CommandType = xlCmdSql
End With
ActiveWorkbook.Connections("Connection1").Refresh
Folder = "U:\" 'Path in U drive
Filename = "Filename" & ".xls"
fpath = Folder & Filename
cn.Open strCon
cn.CommandTimeout = 0
rs.ActiveConnection = cn
rs.Open strSQL
Dim vR() As Variant
Dim str As String, i As Integer
For i = 0 To rs.Fields.Count - 1
ReDim Preserve vR(i)
vR(i) = rs.Fields(i).Name
Next
Set fs = CreateObject("Scripting.FileSystemObject")
Set A = fs.CreateTextFile(fpath)
str = Join(vR, vbTab) & vbCrLf
A.Write str
A.Write (rs.GetString(adClipString, , , vbCrLf, ""))
rs.Close
cn.Close
Set cn = Nothing
End Sub
Second full code.
Sub TEXT2()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strCon, strSQL As String
Dim MRC As Variant
strCon = "some_string_connection"
MRC = "" & Worksheets("SQL Text").Range("D4").Value & ""
strSQL = MRC 'Sql Query
Sheets("Summary").Select
With ActiveWorkbook.Connections("Connection1").OLEDBConnection
.CommandText = HR
.CommandType = xlCmdSql
End With
ActiveWorkbook.Connections("Connection1").Refresh
Folder = "U:\" 'Path in U drive
Filename = "Filename" & ".xls"
fpath = Folder & Filename
cn.Open strCon
cn.CommandTimeout = 0
rs.ActiveConnection = cn
rs.Open strSQL
Dim WB As Workbook, Ws As Worksheet
Dim i As Integer
Set WB = Workbooks.Add(Template:=xlWorksheet)
Set Ws = ActiveSheet
If Not rs.EOF Then
With Ws
For i = 0 To rs.Fields.Count - 1
.Cells(1, i + 1).Value = rs.Fields(i).Name
Next
.Range("a2").CopyFromRecordset rs
.Columns.AutoFit
End With
Else
MsgBox "There is no record!", vbCritical
End If
WB.SaveAs fpath
WB.Close (0)
rs.Close
cn.Close
Set cn = Nothing
End Sub

Syntax error in dynamic SQL string

Please help to fix the following syntax error with Like statement. The query works with = but I need to use Like to search in the AAchange field. I think the problem is here "WHERE [AAchange] LIKE '" & "%" & _
but I'm not sure how to correct this syntax. Please see the code below:
Sub ColorNewVariant()
Dim PolicyNum As Variant
Dim bFound As Boolean
Dim cnn As ADODB.Connection 'dim the ADO collection class
Dim rs As ADODB.Recordset 'dim the ADO recordset class
Dim dbPath As String
Dim strSQL As String
Dim r As Range, cell As Range
Dim LastRow As Long
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
Application.EnableEvents = False
Set r = ThisWorkbook.ActiveSheet.Range("G3:G" & LastRow)
For Each cell In r
If cell.Value <> "" Then
PolicyNum = cell.Value
dbPath = PATH_MAIN & "\Report\MDL_IonTorrent.accdb"
Set cnn = New ADODB.Connection ' Initialise the collection class variable
'Connection class is equipped with a -method- Named Open
'--4 aguments-- ConnectionString, UserID, Password, Options
'ConnectionString formula--Key1=Value1;Key2=Value2;Key_n=Value_n;
cnn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbPath
strSQL = "SELECT [AAchange] " & _
"FROM [MDL_Table1] " & _
"WHERE [AAchange] LIKE '" & "%" & _
Replace(PolicyNum, """", """""", , , vbTextCompare) & _
""""
'Create the ADODB recordset object.
Set rs = New ADODB.Recordset 'assign memory to the recordset
'ConnectionString Open '--5 aguments--
'Source, ActiveConnection, CursorType, LockType, Options
rs.Open strSQL, cnn
bFound = Not rs.EOF
'Check if the recordset is empty.
'Close the recordet and the connection.
rs.Close
cnn.Close
'clear memory
Set rs = Nothing
Set cnn = Nothing
'Enable the screen.
If bFound Then
'MsgBox "Record exists."
Else
'MsgBox "Record not found."
'cell.Interior.ColorIndex = 8
cell.Interior.Color = RGB(255, 217, 218)
'cell.ClearComments
'cell.AddComment "New Variant"
'Fits shape around text
'cell.Comment.Shape.TextFrame.AutoSize = True
End If
End If
Next cell
Application.EnableEvents = True
End Sub
Change the quoting in your query's WHERE clause.
If you use single quotes to start and end the string value you build, you needn't bother with Replace() of double quotes within the PolicyNum value. That should make this task simpler and less confusing ...
strSQL = "SELECT [AAchange] " & _
"FROM [MDL_Table1] " & _
"WHERE [AAchange] LIKE '%" & PolicyNum & "'"
Debug.Print strSQL

Using Excel VBA to export data to MS Access table

I am currently using following code to export data from worksheet to MS Access database, the code is looping through each row and insert data to MS Access Table.
Public Sub TransData()
Application.ScreenUpdating = False
Application.EnableAnimations = False
Application.EnableEvents = False
Application.DisplayAlerts = False
ActiveWorkbook.Worksheets("Folio_Data_original").Activate
Call MakeConnection("fdMasterTemp")
For i = 1 To rcount - 1
rs.AddNew
rs.Fields("fdName") = Cells(i + 1, 1).Value
rs.Fields("fdDate") = Cells(i + 1, 2).Value
rs.Update
Next i
Call CloseConnection
Application.ScreenUpdating = True
Application.EnableAnimations = True
Application.EnableEvents = True
Application.DisplayAlerts = True
End Sub
Public Function MakeConnection(TableName As String) As Boolean
'*********Routine to establish connection with database
Dim DBFullName As String
Dim cs As String
DBFullName = Application.ActiveWorkbook.Path & "\FDData.mdb"
cs = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & DBFullName & ";"
Set cn = CreateObject("ADODB.Connection")
If Not (cn.State = adStateOpen) Then
cn.Open cs
End If
Set rs = CreateObject("ADODB.Recordset")
If Not (rs.State = adStateOpen) Then
rs.Open TableName, cn, adOpenKeyset, adLockOptimistic
End If
End Function
Public Function CloseConnection() As Boolean
'*********Routine to close connection with database
On Error Resume Next
If Not rs Is Nothing Then
rs.Close
End If
If Not cn Is Nothing Then
cn.Close
End If
CloseConnection = True
Exit Function
End Function
Above code works fine for few hundred lines of records, but apparently it will be more data to export, Like 25000 records, is it possible to export without looping through all records and just one SQL INSERT statement to bulk insert all data to Ms.Access Table in one go?
Any help will be much appreciated.
EDIT: ISSUE RESOLVED
Just for information if anybody seeks for this, I've done a lots of search and found the following code to be work fine for me, and it is real fast due to SQL INSERT, (27648 records in just 3 seconds!!!!):
Public Sub DoTrans()
Set cn = CreateObject("ADODB.Connection")
dbPath = Application.ActiveWorkbook.Path & "\FDData.mdb"
dbWb = Application.ActiveWorkbook.FullName
dbWs = Application.ActiveSheet.Name
scn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & dbPath
dsh = "[" & Application.ActiveSheet.Name & "$]"
cn.Open scn
ssql = "INSERT INTO fdFolio ([fdName], [fdOne], [fdTwo]) "
ssql = ssql & "SELECT * FROM [Excel 8.0;HDR=YES;DATABASE=" & dbWb & "]." & dsh
cn.Execute ssql
End Sub
Still working to add specific fields name instead of using "Select *", tried various ways to add field names but can't make it work for now.
is it possible to export without looping through all records
For a range in Excel with a large number of rows you may see some performance improvement if you create an Access.Application object in Excel and then use it to import the Excel data into Access. The code below is in a VBA module in the same Excel document that contains the following test data
Option Explicit
Sub AccImport()
Dim acc As New Access.Application
acc.OpenCurrentDatabase "C:\Users\Public\Database1.accdb"
acc.DoCmd.TransferSpreadsheet _
TransferType:=acImport, _
SpreadSheetType:=acSpreadsheetTypeExcel12Xml, _
TableName:="tblExcelImport", _
Filename:=Application.ActiveWorkbook.FullName, _
HasFieldNames:=True, _
Range:="Folio_Data_original$A1:B10"
acc.CloseCurrentDatabase
acc.Quit
Set acc = Nothing
End Sub
#Ahmed
Below is code that specifies fields from a named range for insertion into MS Access. The nice thing about this code is that you can name your fields in Excel whatever the hell you want (If you use * then the fields have to match exactly between Excel and Access) as you can see I have named an Excel column "Haha" even though the Access column is called "dte".
Sub test()
dbWb = Application.ActiveWorkbook.FullName
dsh = "[" & Application.ActiveSheet.Name & "$]" & "Data2" 'Data2 is a named range
sdbpath = "C:\Users\myname\Desktop\Database2.mdb"
sCommand = "INSERT INTO [main] ([dte], [test1], [values], [values2]) SELECT [haha],[test1],[values],[values2] FROM [Excel 8.0;HDR=YES;DATABASE=" & dbWb & "]." & dsh
Dim dbCon As New ADODB.Connection
Dim dbCommand As New ADODB.Command
dbCon.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & sdbpath & "; Jet OLEDB:Database Password=;"
dbCommand.ActiveConnection = dbCon
dbCommand.CommandText = sCommand
dbCommand.Execute
dbCon.Close
End Sub