Alternative to Access app - vba

A subroutine in Excel VBA runs a macro in an Access database. This exports a table in the db to a .csv file. I have Access installed on my PC but other people that will also need to use this do not, so see an error message.
Is there any other method I can use to run a macro from Access besides creating an object as in my code below, maybe something like a ADODB?
My Excel VBA code:
Dim accApp As Object
Set accApp = CreateObject("Access.Application")
dbPath = ThisWorkbook.Path & "\Database\STORE_TOC_FRONTEND.accdb"
accApp.OpenCurrentDatabase dbPath
accApp.DoCmd.RunMacro "Create SS Upload"
Set accApp = Nothing

Yeah make an Adodb connection object, and a Adodb recordset object. Untested code below.
set conn = CreateObject("ADODB.Connection")
conn.Provider="Microsoft.Jet.OLEDB.4.0" 'Or Microsoft.ACE.OLEDB.12.0 for 2007+ version of access
conn.Open "c:/webdata/northwind.mdb" 'path to your db
set objRecordset=CreateObject("ADODB.Recordset")
objRecordset.open("Select * from SomeTable",conn,0,3,1)
'do you work here, something like a loop for each field and for each row
objRecordset.close
conn.close

Related

I'm trying to implement late binding on an access front end and can not get a DAO refrence to work

I'm working on making a database late bound, so that when the front end is opened, users with different version of MS Office won't have issues. I keep on getting a run time error 438 (Object doesn't support this properts or method) in this code, on the line with For Each tdf In dbs.TableDefs.
I can't see what is going wrong here. Everything is declared and it should find it. Can someone point out what might be happening?
Function RelinkTables()
On Error GoTo EndFast
'Routine to relink the tables automatically. Change the constant LnkDataBase to the desired one and run the sub
'DB front end could be used in 2 or more locations with different backends.
Dim dbs As Object
#If VBA7 Then
Set dbs = CreateObject("DAO.DBEngine.120")
#Else
Set dbs = CreateObject("DAO.DBEngine.36")
#End If
' Dim dbs As DAO.Database
' Set dbs = CurrentDb
Dim tdf As Object
'Dim tdf As DAO.TableDef
Dim strTable As String
Dim strLocation As String
'Abandon relinking if file is development version
If VBA.InStr(1, VBA.UCase(GetNamePath), "_DEV") > 0 Then Exit Function
'Get the Path of the Document and form backend name
strLocation = GetFolderFromPath(GetNamePath)
strLocation = FormBackendName(strLocation)
'Go about relinking
For Each tdf In dbs.TableDefs
If VBA.Len(tdf.Connect) > 1 Then 'Only relink linked tables
'If tdf.Connect <> ";DATABASE=" & LnkDataBaseDubai Then 'only relink tables if the are not linked right '' With PW, Access wont relink, even when the PW is Correct. MUST RELINK!
If VBA.Left(tdf.Connect, 4) <> "ODBC" Then 'Don't want to relink any ODBC tables
strTable = tdf.Name
'dbs.TableDefs(strTable).Connect = ";DATABASE=" & strLocation & ";PWD=" & DatenbankPW 'With password
dbs.TableDefs(strTable).Connect = ";DATABASE=" & strLocation & ";" 'Without password
dbs.TableDefs(strTable).RefreshLink
End If
'End If
End If
Next tdf
dbs.Close
Exit Function
EndFast:
On Error GoTo 0
MsgBox "The backend database was not found. Without the backend this database does not work." & vbCrLf _
& "" & vbCrLf _
& "Ensure that an Access Backend DB is located in the a subfolder called: ""_Sources"" and that read and write permission for the folder are granted." & vbCrLf _
& "" & vbCrLf _
& "Contact the developer if further support is needed.", vbOKOnly Or vbExclamation Or vbSystemModal Or vbMsgBoxSetForeground, "Database backend not found"
End Function
You need to actually open a database if you want to use tables.
You have set dbs to be a database engine, not a database.
If you want it to be the current database, just set it as such, no early binding needed:
Dim dbs As Object
Set dbs = CurrentDb
Else, open up a database:
Dim dbs As Object
Dim dbe As Object
#If VBA7 Then
Set dbe = CreateObject("DAO.DBEngine.120")
#Else
Set dbe = CreateObject("DAO.DBEngine.36")
#End If
Set dbs = dbe.OpenDatabase("C:\Some database.mdb")
' Must be mdb since DAO.DBEngine.36 doesn't support accdb
Access does not require a reference to the DAO library anymore (since Access 2007, the DAO library is built in). so REMOVE the dao reference. You don't need it, and late binding will NOT help nor change the broken reference issue.
So, you will note the references are now this:
You cannot remove the ACE data engine reference, but you REALLY want to make sure you do NOT reference the DAO object library.
You ONLY reference DAO if you using a pre-2007 database, and ALSO that you NOT use ACE, and using a mdb file.
For accDB, and access 2007 onwards?
The Access team now owns DAO for Access and they do NOT update nor maintain the external DAO library now. DO NOT reference DAO library.
So, DAO is now built into Access. (but yes, you do have to referance the ACE data engine now)
However, EVEN with the above references, you can, and should make a habit of referencing DAO in your code, since it not only a good habit, but also gives you intel-sense as a bonus.
Good idea to have a option explict in your code modules.
So, no need to late bind DAO, since you don't need the reference to DAO anymore!!

Access 2016 - RecordsetClone Error on a form constructed with a virtual recordset opens "Select Data Source" dialog

I've seen a number of posts trying to describe this bug but they haven't framed the problem correctly to be reproduced... or not set the scenario in the way that I've experienced the bug using a common technique.
The bug occurs when a form's recordset is set to a virtual recordset and then referred to by a DAO recordsetclone statement. Instead of the recordset being set to the form's recordset (via cloning), a "Select Data Source" dialog is presented.
We most commonly use this to add a checkbox control to a detail form for a user to select one or more records for further processing. I've used this technique many times in many applications but now it fails.
Note: I have confirmed that this code works correctly in Access 2010.
I'm using Windows 10 Pro with a 32 bit Office installation
To set this up and reproduce the bug:
Create a new ACCDB database
Add the following references to the default references:
Microsoft ActiveX Data Objects 6.1 Library
Microsoft ADO Ext. 2.8 for DDL and Security
Create a testing table:
TestId, AutoNumber, PK
TestText, Short Text
Append about 10 rows to the table.
Create an unbound form with 3 controls:
Checkbox, Name: Selected, Control Source: Selected
Textbox, Name: TestId, Control Source: TestId
Textbox, Name: TestText, Control Source: TextText
In the form's header add a command button: Name: cmdTest, Caption: Test
Set the form Default View: Continuous
In the Form_Open call a sub SetRecordsource which creates a recordset and adds a column "Selected" for the user to check the records they want.
The command button cmdTest will attempt to reference the form's recordsource. It's while attempting to reference the form's recordsouce that the error occurs. Instead of the reference being made, the "Select Data Source" dialog pops up.
The complete form's VBA code:
Option Compare Database
Option Explicit
Private Sub cmdTest_Click()
On Error GoTo errHandler
Dim rs As DAO.Recordset
Set rs = Me.RecordsetClone
' Using an ADODB recordset works but is an ugly solution
' To test comment out the Dim DAO and Set rs statements above and uncomment the next 2 lines.
' Dim rs As ADODB.Recordset
' Set rs = Me.Recordset
rs.MoveFirst
With rs
Do While Not .EOF
Debug.Print .Fields("Selected"), .Fields("TestId"), .Fields("TestText")
.MoveNext
Loop
End With
Set rs = Nothing
ExitSub:
Exit Sub
errHandler:
MsgBox "Error in " & Me.Name & ".SetRecordsource " & Err.Number & " - " & Err.Description
Resume ExitSub
End Sub
Private Sub SetRecordsource()
Dim rs As ADODB.Recordset 'the virtual recordset to hold the source data plus the boolean Selected field
Dim rsSource As DAO.Recordset 'dim the source recordset
Set rs = New ADODB.Recordset
With rs
.Fields.Append "Selected", adboolean
.Fields.Append "TestId", adInteger, , adFldKeyColumn
.Fields.Append "TestText", adVarChar, 80
.CursorLocation = adUseClient
.LockType = adLockOptimistic
.CursorType = adOpenKeyset
.Open
Set rsSource = CurrentDb.OpenRecordset("Select TestId, TestText from Test", dbOpenDynaset)
rsSource.MoveFirst
Do Until rsSource.EOF
.AddNew
.Fields("Selected") = 0 'set the checkboxes to unchecked
.Fields("TestId") = rsSource.Fields(0)
.Fields("TestText") = rsSource.Fields(1)
.Update
rsSource.MoveNext
Loop
End With
Set Me.Recordset = rs 'Set the form's recordset = to our virtual recordset
Set rsSource = Nothing
Set rs = Nothing
ExitSub:
Exit Sub
err_handler:
MsgBox "Error in " & Me.Name & ".SetRecordsource " & Err.Number & " - " & Err.Description
Resume ExitSub
End Sub 'SetRecordsource
Open the form and click the Test command button to reproduce the error.
One solution proposed is to use an ADODB recordset and set it to Me.Recordset instead of Me.Recordsetclone. While this does work, it's an ugly solution since you are now operating on the form's recordsource and when looping through the records to find the rows where Selected = True moves the current record on the form. Not only does the current record pointer move but if there's more rows then the can show, the user sees the form's records scrolling.
Any help, confirmation or recommendations would be greatly appreciated.
Thanks in advance!
From another forum the solution to this is to use an ADODB recordset and then clone the form to it via Recordset.Clone. In the code above, it references an "ugly" solution:
' Using an ADODB recordset works but is an ugly solution
' To test comment out the Dim DAO and Set rs statements above and uncomment the next 2 lines.
' Dim rs As ADODB.Recordset
' Set rs = Me.Recordset
Setting rs = Me.Recordset will operate on the form (not desired).
But using an ADODB recordset and then
setting rs = Me.Recordset.Clone works, does not operate on the form and doesn't pop up the Data Source Dialog.
Something has changed in 2016 but this does work and may help someone else.
You may also want to read: Create In-Memory ADO Recordsets at Database Journal
Your code can't work, as you try to assign an ADODB.Recordset (the one in Form.Recordset) to a DAO.Recordset,`as it is declared.
If the Recordset-Type can vary, you can dimrs as Objectthen it gets the type of Form.Recordset(by Form Property RecordsetClone, that surprisingly works for ADODB:Recordsets too). You can query the type with:
If TypeOf Me.RecordSet Is ADODB.Recordset Then
'ADODB
Else
'DAO
End If
If you need an unboundCheckBox, you can useclsCCRecordSelect-Class from SelectRecordsV2.
TheclsCCRecordSelectis used by me for years and I don't want to live without!

Creating a Button in Excel, that calls upon a created Access query

Is there a way to create a button in Excel that runs a query, that is already created, in Access, and then updates the excel spreadsheet using the data from the query? I've searched the web for directions on how to do this, but have only found answers that create a button in Excel, that only runs a query in Excel, not Access. I am assuming this will be done by coding, upon click, in VBA, but have yet to find anything that does this. So... Is it possible? If so, how?
Okay, so I have kind of updated this with question, because I sort of used both options made. So I first created a Function in a Standard Module (Because we may use this later for another sheet in the workbook, and we didn't want to duplicate work):
Function GetSqlServerData(sQuery As String, sRange As Range)
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
' Create the connection string.
sConnString = "NMS"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Open the connection and execute.
conn.Open sConnString
Set rs = conn.Execute(sQuery)
' Check we have data.
If Not rs.EOF Then
' Transfer result.
Sheets(3).Range("A2").CopyFromRecordset rs
' Close the recordset
rs.Close
Else
MsgBox "Error: No records returned.", vbCritical
End If
' Clean up
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rs = Nothing
End Function
Then I tried to use said function:
Sub GetPermits()
Dim sQuery As String
Dim sRange As Range
Set sQuery = "Select * From Customer;"
Set sRange = Sheets(3).Range("A2")
GetSqlServerData(sQuery, sRange)
End Sub
But it gives me an error right at the spot where is actually use the function. I don't need it to go to a MsgBox, and I don't need it to print it out, all I need is for it to put in the data into the sheet noted on the function call. BTW, the function needs some tweeking, I know. Right now, I just need it to call the darn thing, lol.
Here is a Screen Shot of the error message: If you cant see it, it says, "Compile Error: Expected:=" and it highlights the "GetSqlServerData(sQuery, sRange)" in red. So it must have something to do with that. I just can't figure out what it is.
Screenshot of the error message
Dependent on your requirements, you could have this without VBA in a quicker and more reliable way, to have a table that is pointed at your query, that updates when you click Refresh.
To do so, in Excel navigate to Data > From Access.
From here, navigate to your database with the saved query, and when asked to select a table, you can select the query instead.
You can also write your own SQL query for Excel to execute against the database instead.
You can edit the connection properties to refresh when the workbook is opened, or refresh when every 60 minutes for example, or you could turn it all off, and allow the user to hit 'Refresh' in Excel itself.
Alternatively, you could setup a button that runs the refresh table command against the linked table, and this would do the same
Private Sub CommandButton1_Click()
ActiveWorkbook.RefreshAll
End Sub
Good luck.
As an example for a solution with VBA using ADODB one could use the following function to connect to the database.
Function ConnectToDB(ByVal fileName As String)
Dim conn As New ADODB.Connection
If Dir(fileName) = "" Then
MsgBox "Could not find file " & fileName
Exit Function
End If
Dim connectionString As String
' https://www.connectionstrings.com/access/
connectionString = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" _
& fileName & ";Persist Security Info=False;"
conn.Open connectionString
Set ConnectToDB = conn
End Function
And if you want to copy data from the database one could use the following code. You need to have a sheet with the codename shRepAllRecords
Option Explicit
Sub ReadFromDB()
' Get datbase name
Dim dbName As String
dbName = <fule filename of the database>
' Connect to the databse
Dim conn As ADODB.Connection
Set conn = ConnectToDB(dbName)
' read the data
Dim rs As New ADODB.Recordset
Dim query As String
' First example to use an SQL statement
' query = "SELECT * From CUSTOMER"
' Second example to use a query name defined in the database itself
query = "qryCustomer"
rs.Open query, conn
' shRepAllRecords is the codename of the sheet where the
' data is written to
' Write header
Dim i As Long
For i = 0 To rs.Fields.Count - 1
'shRepAllRecords.Cells(1, i + 1).Value = rs.Fields(i).Name
shRepAllRecords.Range("A1").Offset(0, i) = rs.Fields(i).Name
Next i
' Write Data
shRepAllRecords.Range("A2").CopyFromRecordset rs
shRepAllRecords.Activate
' clean up
conn.Close
End Sub
So I was finally able to figure out the issue. I was not putting "Call" in front of the function call. Once I did that, it accepted it. Thanks for all the assistance! The final answer was close to what Storax gave above. So I credited him with the answer. Thanks again!

Importing .csv to MS Access. "AtEndOfStream" doesn't read last line

I want to import some data from a .csv file into a MS Access database via VBScript.
Sub CSVImport
connStr = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=P:\somedatabase.accdb"
'Define object type
Set objConn = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")
'Open Connection
objConn.open connStr
objRecordSet.Open "SELECT * FROM SomeTable", _
objConn, adOpenStatic, adLockOptimistic
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("P:\someFile.csv")
Do Until objFile.AtEndOfStream
strVKBL_Stamm = objFile.ReadLine
arrVKBL_Stamm = Split(strVKBL_Stamm, ";")
objRecordSet.AddNew
objRecordSet("FirstName") = arrVKBL_Stamm(0)
objRecordSet("LastName") = arrVKBL_Stamm(1)
objRecordSet("Date") = arrVKBL_Stamm(...)
objRecordSet("Type") = arrVKBL_Stamm(11)
Loop
Set objRecordSet = Nothing
Set objFSO = Nothing
Set objFile = Nothing
Set objConn = Nothing
End Sub
This script gets all the data out of my CSV file but it does miss the last line. When I hit return twice (once doesn't suffice) at the end of the .csv all of my data gets imported.
My backup plan is to change the AtEndOfStream bit to something like
If arrVKBL_Stamm(0) = 0 Then
Exit Do
and add a zero to the file. Problem is, the .csv is a report, exported from SAP (which is - afaik - unable to export to MS Access itself), so that part would need another script or has to be done by hand.
Does anyone have a clue or give me a hint how to solve this?
I tried this code:
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("D:\myfile.csv", 1)
Do Until objFile.AtEndOfStream
S = objFile.ReadLine
Debug.Print S
Loop
objFile.Close
in VBA and it printed all lines of my CSV file, even if the last line doesn't end with a CrLf.
I'm pretty sure the reason is: your objRecordSet.AddNew is missing an objRecordSet.Update.
Apparently, if you call .AddNew after the previous .AddNew, the previous record is saved nevertheless.
But objRecordSet.AddNew followed by Set objRecordSet = Nothing doesn't save the last record.
Solution: add objRecordSet.Update as last command in the loop.
SAP uses a standard database engine in the back end. You can set up linked tables that pull directly from SAP in to Access. No export\import. It's a live table.
SEE HERE:
How do I configure my Microsoft Access database to pull source data directly from SAP BW?
https://archive.sap.com/discussions/thread/1382889

Open field in protected, shared Excel workbook

I have a shared, protected workbook that has a button to bring up a search form. There are two fields on this form, txtYear and cbxRegion, that I need enabled. Whenever I try to open the fields, it works until I exit Excel.
I have tried unprotecting the workbook, unsharing it, and commenting out any reference in the VBA to reprotecting the form. And still, even the edited VBA reverts back to the original.
This is the section of code referring to the form I need enabled. Any assistance would be greatly appreciated. I'm using Excel 2010.
Private Sub UserForm_Initialize()
Dim strDb As String
Dim rs As ADODB.Recordset
Dim cn As ADODB.Connection
Dim row As Integer
Dim AccessVersionID As String
cbxRegion.Value = Worksheets("Parameters").Cells(5, 14)
Me.txtYear = Worksheets("Parameters").Cells(4, 7)
Me.chkBoth = Worksheets("Parameters").Cells(9, 2)
Me.chkConsultant = Worksheets("Parameters").Cells(7, 2)
Me.chkInHouse = Worksheets("Parameters").Cells(8, 2)
'Set region values
'Open connection
'Select Case SysCmd(acSysCmdAccessVer)
'Case 11: AccessVersionID = "2003"
'End Select
'If AccessVersionID = "2003" Then
' strDb = Worksheets("Parameters").Cells(17, 2).Value 'This will reference the path
'Else
strDb = Worksheets("Parameters").Cells(18, 2).Value
'End If
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & strDb & ";"
Set rs = New ADODB.Recordset
'Get recordset
With rs
Set .ActiveConnection = cn
.Open "Select * From LookupRegion"
.Requery
End With
'Add regions
row = 0
With rs
.MoveFirst
Do Until .EOF
cbxRegion.AddItem ![region]
cbxRegion.list(row, 1) = ![RegionName]
row = row + 1
.MoveNext
Loop
End With
'Close the recordset
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
I managed to get it to work. In order, I:
Unshared the workbook
Unprotected the workbook
Saved, closed, and reopened the workbook to make sure the settings stayed
Enabled the fields
Saved, closed, and reopened the workbook to make sure the settings stayed
Protected the workbook
Shared the workbook (as required by the specifications I was given, I would rather not share it but it really isn't my call here)
Saved, closed, and reopened the workbook to make sure the settings stayed
And now it works exactly as I need it to.
As far as I am concerned, the best advice anyone can give you for a shared workbook is: don't use them.
Shared Workbooks are impossible to troubleshoot. Their aberrant behaviour cannot be fixed. They don't follow any logic. Once a shared workbook starts acting up and behaving strangely, you have reached the inevitable end stage. Nothing can be done to fix it. The behaviour is not necessarily reproducible.
If you need simultaneous multi-user write access to a dataset, then Excel is the wrong tool. Use a database.
Don't use shared workbooks.