Import Selected Columns to Excel from Access Table using VBA - sql

I am trying to import selected data from an access table. This table has 4 columns and I want only want columns 2 and 3. In Excel and want them listed in the order: column 3, column 2 (reverse to how they are in Access). Additionally I want to select rows (From Access table) based on a date referenced in the Excel Spread sheet (which I refer to as RpDate in the code). In Access, "Date" is the first column. I need some help please. Thanks.
Sub ADOImportFromAccessTable()
Dim DBFullName As String
Dim TableName As String
Dim TargetRange As Range
Dim RpDate As Range
DBFullName = "C:\Documents\Database.mdb"
TableName = "DataTable"
TargetRange = Range("C5")
RpDate = Range("B2").Value
Dim cn As ADODB.Connection, rs As ADODB.Recordset, intColIndex As Integer
Set TargetRange = TargetRange.Cells(1, 1)
' open the database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & _
"C:\Documents\Database.mdb" & ";"
Set rs = New ADODB.Recordset
With rs
' open the recordset
.Open TableName, cn, adOpenStatic, adLockOptimistic, adCmdTable
' all records
.Open "SELECT * FROM " & TableName & _
" WHERE [Date] = RpDate, cn, , , adCmdText"
' filter rows based on date
rs.Open , TargetRange
End With
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub

Try this
Sub ADOImportFromAccessTable()
Dim DBFullName As String
Dim TableName As String
Dim TargetRange As Range
Dim RpDate As Range
DBFullName = "C:\Documents\Database.mdb"
TableName = "DataTable"
Set TargetRange = Range("C5")
RpDate = Range("B2").Value
Dim cn As ADODB.Connection, rs As ADODB.Recordset, intColIndex As Integer
Set TargetRange = TargetRange.Cells(1, 1)
' open the database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & _
"C:\Documents\Database.mdb" & ";"
Set rs = New ADODB.Recordset
With rs
' open the recordset
.Open TableName, cn, adOpenStatic, adLockOptimistic, adCmdTable
' all records
.Open "SELECT Time, Tank FROM " & TableName & " WHERE [Date] = " & RpDate & " ORDER BY Tank, Time", cn, , , adCmdText
' filter rows based on date
End With
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
This isn't prof to SQL injection, but is a start

Related

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

Run a excel macro with sql quicker?

This code do the work but takes 10 minutes to run. There is maybe a way in the sql part to make it faster. There is not a lot of data so I uspect the sql part.
Dim noCsf As String
Dim cel As Range
Dim rng As Range
Dim noRow As Integer
Set rng = Sheets("CS_A").Range("D5:D68")
Dim targetRng1 As Range
Dim targetRng2 As Range
Dim bd As String
Dim cn As Object
Dim rs1 As Object
Dim rs2 As Object
Dim strSql As String
Dim strConnection As String
Set cn = CreateObject("ADODB.Connection")
Set rs1 = CreateObject("ADODB.Recordset")
Set rs2 = CreateObject("ADODB.Recordset")
bd = "U:\BD\Data_512_P.accdb"
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & bd
For Each cel In rng
If Len(cel.Address) = 4 Then
noRow = Right(cel.Address, 1)
Else
noRow = Right(cel.Address, 2)
End If
noCsf = cel.Value
rs1.Open "SELECT SommeDetotal_euaii FROM Rqt_CS_Anglo WHERE Expr1 LIKE '" & noCsf & "' ", cn, , , adCmdText
Set targetRng1 = Sheets("CS_A").Range("E" & noRow)
targetRng1.CopyFromRecordset rs1
rs1.Close
rs2.Open "SELECT SommeDeeua_apres_exemption FROM Rqt_CS_Anglo WHERE Expr1 LIKE '" & noCsf & "' ", cn, , , adCmdText
Set targetRng2 = Sheets("CS_A").Range("F" & noRow)
targetRng2.CopyFromRecordset rs2
rs2.Close
noRow = noRow + 1
Next
Debug.Print "DONE"
Set rs1 = Nothing
Set rs2 = Nothing
cn.Close
Set cn = Nothing
I expect a quicker running time maybe the sql part could be improve the fact in take data from a access request
Using a single query per line:
Const BD As String = "U:\BD\Data_512_P.accdb"
Dim cel As Range
Dim cn As Object
Dim rs As Object
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & BD
For Each cel In Sheets("CS_A").Range("D5:D68").Cells
rs.Open "SELECT SommeDetotal_euaii, SommeDeeua_apres_exemption FROM " & _
"Rqt_CS_Anglo WHERE Expr1 LIKE '" & cel.Value & "' ", cn, , , adCmdText
If Not rs.EOF Then
With cel.EntireRow
.Cells(5).Value = rs.Fields("SommeDetotal_euaii").Value
.Cells(6).Value = rs.Fields("SommeDeeua_apres_exemption").Value
End With
End If
rs.Close
Next cel
Depending on the size of the source table it may be quicker to build (eg) a lookup table using a scripting dictionary than to make repeated queries to the database.
If the database is on a mapped drive then creating a [temporary] local copy will likely speed things up.
If that still doesn't help then you can add more details about how many rows you're processing, are there any duplicates, and what's the size of your source DB table.

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.

Export from Excel to AccessDB, error Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another

I try to export some data from excel to my access database, but on line 15 rs.open I get the error Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another. I can't seem to figure out what is going wrong here. Any help would be appreciated, thanks!
Public Sub updateAntibiotics(abName As String, Optional startDate As Date, Optional stopDate As Date)
Dim cn As Object, rs As Object
Dim currPath As String, DbPath As String
Dim sProduct As String, sVariety As String, cPrice As Variant
Dim patientID As Integer
' connect to the Access database
currPath = Application.ActiveWorkbook.Path
DbPath = Left$(currPath, InStrRev(currPath, "\")) & "IZ Damiaan.accdb"
Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=Microsoft.Ace.OLEDB.12.0;Data Source='" & DbPath & "';"
' open a recordset
Set rs = CreateObject("ADODB.Recordset")
rs.Open "Antibiotics", cn, adOpenKeyset, adLockOptimistic, adCmdTable
patientID = Val(Sheets("PatientData").Range("A2"))
rs.Filter = "fkPatientID='" & patientID & "' AND Antibiotic='" & abName & "' AND stopDate IS NULL"
If rs.EOF Then
Debug.Print "No existing record - adding new..."
rs.Filter = ""
rs.AddNew
rs("fkPatientID").Value = patientID
rs("Antibiotic").Value = abName
Else
Debug.Print "Existing record found..."
End If
If Not IsNull(startDate) Then rs("startDate").Value = startDate
If Not IsNull(stopDate) Then rs("stopDate").Value = stopDate
rs.Update
Debug.Print "...record update complete."
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub

SQL ADODB Recordset select distinct is in alphabetical order

I'm using an ADODB record set in VBA. When I use Select Distinct in a SQL query of the ADODB record set the results come out in alphabetical order. I need the results in the order they are in the data. Is that possible?
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
DatPath = ThisWorkbook.Path & "\Temp\" & TB.Name
strCon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & DatPath & ";Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1"";"
cn.Open strCon
strSQL = "SELECT Distinct(Period) from [Data$] "
rs.Open strSQL, cn, 3, 3
ThisWorkbook.Sheets("ADO Out").Cells.Clear
ThisWorkbook.Sheets("ADO Out").Activate
ThisWorkbook.Sheets("ADO Out").Cells(1, 1).CopyFromRecordset rs
Here are two workarounds that both involve retrieving all Period records.
Range.RemoveDuplicates will remove the duplicates while preserving the order
Sub UnorderedPeriod()
Dim DatPath As String, strCon As String, strSQL As String
Dim cn As Object, rs As Object
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
DatPath = ThisWorkbook.Path & "\Temp\" & TB.Name
strCon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & DatPath & ";Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1"";"
cn.Open strCon
strSQL = "SELECT [Period] from [Data$]"
rs.Open strSQL, cn, 3, 3
With ThisWorkbook.Sheets("ADO Out")
.Cells.Clear
.Cells(1, 1).CopyFromRecordset rs
.Activate
.Columns(1).RemoveDuplicates Columns:=1
End With
End Sub
Use an ArrayList to remove the duplicates
Function CopyDistinctFromRecordset(rs As Object, Target As Range)
Dim list As Object
Dim data
Dim x As Long
rs.MoveFirst
data = rs.getRows
Set list = CreateObject("System.Collections.ArrayList")
For x = 0 To UBound(data, 2)
If Not list.Contains(data(0, x)) Then list.Add data(0, x)
Next
Target.Resize(list.Count).Value = Application.Transpose(list.ToArray)
End Function