Multiple simultaneous connections from excel vba to Access DB - vba

I am building a somewhat complex Excel sheet for a bank, that uses an Access Database.
This sheet loads a ton of data to prebuilt sheets.
After that, they can run a few macros and add some inputs
In the end, I open a new connection to insert these inputs in different tables from the ones that where queried in the beginning
My problem is that when loading the information to the workbook, this action can only be performed by one user at a time.
When this process is running in one user computer other users get the error
The Microsoft Office Access database engine cannot open or write to
the file ''. It is already opened exclusively by another user, or you
need permission to view and write its data.
I know the users have permission because once the process is done on user 1, user 2 has no problem.
I am using ADODB (connection, recordset and command) and this is the code I use about four different times for four different stored queries in access:
Dim cnn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim cmd_apoio As New ADODB.Command
Set cnn = New ADODB.Connection
cnn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source = " & PV.dbpath
With cmd_apoio
.ActiveConnection = cnn
.CommandType = adCmdStoredProc
.CommandText = "tabela_apoio_entidades"
.Parameters.Append .CreateParameter("nip", adNumeric, adParamInput, 20)
.Parameters.Append .CreateParameter("anomes", adNumeric, adParamInput, 20)
.Parameters(0) = PV.nip_grupo
.Parameters(1) = PV.ano_mes
End With
Set rs = cmd_apoio.Execute()
(the PV. variables are public variables declared in another module(PV))
Using this method is there a way to allow multiple connections at once?
Especially as this is only to retrieve information (read-only) and not to update any records in tables.
I am using Excel 2013 and Access 2013, the database is .accdb
EDIT: The tables are linked tables to txt files (i think this might be important)

Try using rs.Open instead of cmd.Execute, and specify the lock type:
After your End With:
Set rs = New ADODB.Recordset
rs.CursorLocation = adUseClient
rs.Open cmd_apoio, cnn, adOpenStatic, adLockOptimistic

You need to set your connection mode explicitly:
Set cnn = New ADODB.Connection
cnn.Mode = 16 + 3 'adModeShareDenyNone + adModeShareReadWrite '
cnn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source = " & PV.dbpath
See here: https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/connectmodeenum

I can only contribute the comments that I've always considered excel not to be multi user.
And in linking Excel to Access - which I've done a lot - the excel must always be closed when working from the Access side. There cannot be a user directly in the Excel - it's role to Access is to behave like a table.
You are in the other direction in using Excel as the front end with Access tables linked - - but in terms of multi user I wonder if the situation is the same.

I am in the same situation as you do, having excel as front-end connecting to data stored in Access.
I kind of solved this multi-user issue by establishing and closing connection on every single action taken by user.
It drags down performance a lot, but I guess it enables concurrent uses.
cnn.Close
Set cnn = Nothing

Related

ODBC connection from MS Access to MySQL server fails to execute stored procedure

I just started out with mySQL and need some help here. I have a 10.4.27-MariaDB- Server with a mySQL database. I want to execute several queries from a Microsoft Access document. I have a stored procedure on the server named "task_data_proc". This procedure returns the task data for a given task number. It is working fine on the server. It contains the following code:
BEGIN
SELECT * FROM task_data WHERE TASKNUMBER = uTasknumber;
END
In my VBA-code I tried to call the Procedure via ODBC using ADO:
Private Sub Test()
Dim conn As Object, cmd As Object, rst As Object
Const adCmdStoredProc = 4, adParamInput = 1, adVarInt = 3
Dim asked_number As Integer
'defining random task-number for testing purpose
asked_number = 1200
Set conn = CreateObject("ADODB.Connection")
Set rst = CreateObject("ADODB.Recordset")
' DSN-LESS CONNECTION
conn.Open "Driver={MySQL ODBC 8.0 ANSI Driver};host=localhost;database=expert_db;" _
& "UID=root;PWD=generic_password"
' CONFIGURE ADO COMMAND
Set cmd = CreateObject("ADODB.Command")
With cmd
.ActiveConnection = conn
.CommandText = "task_data_proc"
.CommandType = adCmdStoredProc
.CommandTimeout = 15
End With
' APPEND NAMED PARAM
cmd.Parameters.Append cmd.CreateParameter("uTasknumber", adVarInt, _
adParamInput, 6, asked_number)
Set rst = cmd.Execute
' FREE RESOURCES
rst.Close
Set rst = Nothing
Set cmd = Nothing
Set conn = Nothing
End Sub
When I run this I get:
Runtime error '-2147467259 (80004005)':
[MySQL][ODBC 8.0(a) Driver]Access denied for user 'root'#'localhost' (using password: YES)
However I used the same "generic_password" in my code that I use to log into the server with PHPmyAdmin. I tried it without a password as well and I got the same error message, except "(using password: NO)".
I also included the "skip-grant-tables" command in the "my.ini" document for the SQL-Server.
Is the String that I defined for conn.Open incorrect?
I have also tried using a pass-through-query in Access. When I setup the ODBC connection for the pass-through-query I chose the database and hit "Test". It says "connection successful". I hit OK.
The ODBC connection String is then automatically named "ODBC;DSN=unfall;SERVER=localhost;UID=root;PWD={generic_password};DATABASE=expert_db;PORT=3307;COLUMN_SIZE_S32=1;DFLT_BIGINT_BIND_STR=1"
I define the pass-through-query as "SELECT * FROM task_data"
I run the query. It returns the entire table with the task data, as I intended. But if I filter for a specific task number in the table or scroll through the data for a while, suddenly an error pops up:
ODBC-call failed
[MySQL][ODBC 8.0(a) Driver]mysqld-5.5.5-10.4.27-MariaDB
After that message the entire table just contains "#NAME" in every single cell. When I run the query again, the data is back there until I scroll/filter and the message pops up again. Am I using the wrong ODBC driver? Or are pass-through-queries not suitable for select-statements?
I also tried using linked tables with ODBC instead. It worked fine but was quite slow. I wanted to improve the performance by either using pass-through-queries or calling procedures using ADO.
Thank you very much for any help or guidance!

PostgreSQL & Access -- Connecting at start vs. Connect when needed

I have a PostgreSQL 9.5 database with an Access 2016 front-end, using an ODBC driver to connect the two. If I wanted to query data, I would start with the following commands:
Dim conn As New ADODB.Connection
conn.Open "Driver={PostgreSQL ANSI};Server=localhost;Database=[name];User Id=[UID];Password=[pass];"
Dim cmd As New ADODB.Command
cmd.ActiveConnection = conn
My question is this: Is there any reason why I shouldn't establish this connection the moment the application opens, using that connection whenever I need to run a query, as opposed to opening and closing the connection each time I run a query?
I'm unsure what, if any, overhead is involved in establishing such a connection in Access, and I've been unable to find any information on the topic. Apologies if this is a naive question.
I the connection is cached by Access anyway.
Once you touch, or open any linked table, then the connection is now active, and re-used by Access.
In general if the query is against a single table, then there little reason to not just code against the linked table.
Eg:
Dim rst As DAO.Recordset
Dim strSQL As String
strSQL = "select * from tblInvoices where invoiceNum = 13355"
Set rst = CurrentDb.OpenRecordset(strSQL)
If you using a pass-though query, then this works well:
Sub Test555()
Dim rst As DAO.Recordset
With CurrentDb.QueryDefs("qryPass")
.SQL = "select * from tblInvoices where invoiceNum = 13355"
Set rst = .OpenRecordset
End With
End Sub
The reason for above is you thus don’t mess or have to bother with connection strings in your code.
Just ensure that you table re-link code also re-links any pass-through query.

SQL Management Studio 2012 with Excel vba connection string set up

I would like to set up a vba code that would connect to sql management studio 2012 a run the query, which I would specify in the vba code. I have read every similar question here on stack overflow but when I try to replicate them, I always get an error, ussualy that the login failed for user.
I think I am setting up the string connection wrong. Also, I would need the user authentication by Windows authentication.
I know the database name, server name and my user name.
This is the code I am using and which is giving me an error.
Sub ConnectionExample6()
Dim cnn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cnn = New ADODB.Connection
' Open a connection by referencing the ODBC driver.
cnn.ConnectionString = "driver={SQL Server};" & _
"server=SERVER NAME;uid=USER ID;pwd=MyPassword;database=DATABASE NAME"
cnn.Open
' Create a Recordset by executing an SQL statement.
Set rs = cnn.Execute("Select top 100 * from "TABLE NAME" aac " & _
"where aac.EffectiveDate = '10/04/16'")
' Close the connection.
rs.Close
End Sub
Can someone walk me through the connection string and how to set it up step by step? Thank you.
Authentication
If you're connecting to SQL Server, you should prefer Windows Authentication if that's available: you create a Login at server level for a group of Active Directory users, and then you create a Windows-Authenticated User in your database using that login.
That way you are keeping passwords and usernames out of hard-coded strings, and let the network deal with authentication.
Assuming you don't want to be maintaining passwords in dozens of copies of macro-enabled workbooks across your network, you'll want to use Windows Authentication.
Integrated Security=SSPI; Persist Security Info=True;
Server
Connection strings are annoying - seems there's a different format/wording for every single different thing that's able to parse them!
Since you're using ADODB, you'll want to specify a Provider, a Data Source and, optionally, an Initial Catalog:
Provider=SQLOLEDB.1; Data Source=SQL Server instance name; Initial Catalog=Database name;
Who?
Each connection can be monitored on the server; when building your connection string you can optionally specify a Workstation ID to identify the machine the connection is for.
Workstation ID=computer name;
You can get the computer name by fetching the environment variable value, using Environ$:
Private Function GetWorkstationId() As String
GetWorkstationId = Environ$("ComputerName")
End Function
Given a SQL Server instance named SomeSqlServer, a database named SomeDatabase, and using Windows Authentication, the ADODB connection string would look like this:
Dim connString As String
connString = "Provider=SQLOLEDB.1; Data Source=SomeSqlServer; Initial Catalog=SomeDatabase; Integrated Security=SSPI; Persist Security Info=True;"
Given SQL Authentication (with a hard-coded user name and password) for SomeUser with SomePassword:
connString = "Provider=SQLOLEDB.1; Data Source=SomeSqlServer; Initial Catalog=SomeDatabase; UID=SomeUser; PWD=SomePassword;"
Commands
You don't want to be concatenating arbitrary user input into a WHERE clause; avoid executing an SQL string directly from the ADODB.Connection object.
Instead, create an ADODB.Command, and parameterize your query.
Dim sql As String
sql = "SELECT Foo, Bar FROM dbo.FooBars WHERE Foo = ? AND DateInserted > ?"
Here we have 2 parameters.
First we create the command:
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
cmd.CommandText = sql
Then its parameters, assuming we have their respective values in param1Value and param2Value local variables:
Dim param1 As ADODB.Parameter ' a string parameter
Set param1 = New ADODB.Parameter
param1.Type = adVarWChar
param1.Direction = adParamInput
param1.Size = Len(param1Value)
param1.Value = param1Value
cmd.Parameters.Append param1
Dim param2 As ADODB.Parameter ' a date parameter
Set param2 = New ADODB.Parameter
param2.Type = adDate
param2.Direction = adParamInput
param2.Value = param2Value
cmd.Parameters.Append param2
Then we retrieve the recordset by executing the command:
Dim results As ADODB.Recordset
Set results = cmd.Execute
Of course this looks very verbose, but it can easily be refactored into functions dedicated to creating a parameter given a value of a certain type.
As a result, you avoid this situation, because you're no longer executing arbitrary user input concatenated into a query:

Excel VBA SQL Data

I have a small excel program.
I would like to be able to use this program to update a SQL table.
What would be the function to say update line 2 in SQL table Test in Database ABC
Thanks
First of all you need to add a reference to the ActiveX Data Objects library, which contains the set of objects that allow you to do database access - in the Excel Visual Basic editor, go to Tools|References... In the dialog box, scroll down until you find Microsoft ActiveX Data Objects 2.8 Library. Check the box next to the library name.
VBA References dialog with ADO library checked http://philippursglove.com/stackoverflow/adoreference.png
Your code to update the database should then look something like this (using the SQL from JohnK813's answer):
'Declare some variables
Dim cnn As ADODB.Connection
Dim cmd As ADODB.Command
Dim strSQL As String
'Create a new Connection object
Set cnn = New ADODB.Connection
'Set the connection string
cnn.ConnectionString = myDatabaseConnectionString 'See http://connectionstrings.com if you need help on building this string for your database!
'Create a new Command object
Set cmd = New ADODB.Command
'Associate the command with the connection
cmd.ActiveConnection = cnn
'Tell the Command we are giving it a bit of SQL to run, not a stored procedure
cmd.CommandType = adCmdText
'Create the SQL
strSQL = "UPDATE Test SET YourField = NeValue WHERE IDField = 2"
'Pass the SQL to the Command object
cmd.CommandText = strSQL
'Open the Connection to the database
cnn.Open
'Execute the bit of SQL to update the database
cmd.Execute
'Close the connection again
cnn.Close
'Remove the objects
Set cmd = Nothing
Set cnn = Nothing
I see you have other questions open that deal with actually connection to the SQL Server, so I won't add any more to that discussion.
Relational database tables don't think of things as being in a certain order, so you can't really say that a certain record is "record 2" or "line 2" just because you added it to the table second. Unless of course you use a field to create an ID number that increments with each new record added.
Then you can access that record by saying
UPDATE Test SET YourField=NewValue WHERE IDfield=2
Here's more information on the UPDATE command, if you need it.

Connect to a linked table

Connect to a linked table with code.
I have some linked tables from a SQL-server; they are linked with an ODBC connection. The password is not saved with the connection. When I am double clicking on the table in Access table-view I get a prompt for username and password. After entering the password I can view the data in the table.
My problem is when I try to access the table with code before having opened it in this way. What I try to do is to use ADODB to open a recordset with data from the linked table, like:
Dim rst as new ADODB.Recordset
Dim sql as string
Sql = “SELECT * FROM LinkedTable”
rst.Open sql, CurrentProject.Connection, adOpenForwardOnly, adLockReadOnly
Running this code without having access the table before will generate this error: Error# -2147467259, ODBC: connection to dns-name failed.
So, my question is, are there any way to connect to the database with code that can be run when the database is opened? This would also help the users as they would not have to remember a password to the SQL-server.
It seems that you are mixing 2 technologies that might not work together, ie linked tables through ODBC and ADODB recordsets. Have you tried to open DAO recordsets on your linked tables?
Dim rst as DAO.Recordset
Dim sql as string
Sql = “SELECT * FROM LinkedTable”
set rst = currentDb.openRecordset(sql,<your parameters>)
You could of course use ADODB recordsets through 2 ADODB connections, one to your access file, the other one to your SQL server:
Dim rsSQL as ADODB.recordset, _
rsACCESS as ADODB.recordset, _
connectionSQL as ADODB.connection, _
connectionACCESS as ADODB.connection
set connectionSQL = New ADODB.connection
set connectionACCESS = New ADODB.connection
connectionSQL.properties(...) = enumerate your SQL parameters
connectionACCESS.properties(...) = enumerate your ACCESS parameters (use currentproject.accessConnection if your access tables are local tables only)
set rsSQl = New ADODB.recordset
set rsACCESS = New ADODB.recordset
rsSQL.open "SELECT * FROM ...", connectionSQL, <other parameters>
rsACCESS.open "SELECT * FROM ...", connectionACCESS, <other parameters>
Linking ADO recordsets to forms and comboboxes in Access is possible. But, when creating forms, this technology has to be mainly managed through VBA code (you will have to write 'on open' events such as set me.recorset = ...), while the standard "linked tables" technology can be easily used through the user-friendly 'form-design' interface.
You can use a connection string in your code, it is easy enough, seeing you are already using ADO: http://www.connectionstrings.com/
You will need to find out which version of SQL Server you are linking to.