I am building an application, using MS Access as a front-end of a MySQL DB. The application consists of a lot of Forms all of which will execute many different SQL statements.
I am establishing the connection using:
Dim oConn As New ADODB.Connection
Dim Server_Name As String
Dim Database_Name As String
Dim User_ID As String
Dim Password As String
Server_Name = "localhost"
Database_Name = "test"
User_ID = "root"
Password = ""
oConn.Open "DRIVER={MySQL ODBC 5.3 ANSI Driver}" _
& ";SERVER=" & Server_Name _
& ";DATABASE=" & Database_Name _
& ";UID=" & User_ID _
& ";PWD=" & Password _
& ";OPTION=16427"
My questions are:
Is it better to Open and Close the connection each time i run an SQL Statement, or Open the connection, when the Application runs and close when the application closes?
-If the first way is better, Can I Create a global Function that returns a connection, to be used in the current form and not having to write the same code over and over for each form and/or SQL statement?
-If the second way is better, Can I Declare and Open the Connection globally, so It can be used from any form?
Keep in mind that:
-There are 50+ different forms and sub-forms in the application.
-The application should be able to run on multiple computers at once accessing 1 database.
I had this same question and nobody got around to answering it.
In general, its better to keep the connection open when you're going to use it and close it when you're done, but not before then. Which is fine if you use it on a per-form basis but if it gets shared it gets a little more complicated.
What I did initially was open the connection for each form, subforms grabbed the connection from their parent, and the form closes the connection when it gets closed.
The issue, if multiple forms use the same connection, is that if you close that connection, other forms using it will have errors and fail. So, if you want to share the connection between forms you can, but just make sure that it never closes unless the file is being closed. I am currently using this method, since I have a base 'menu' form that can't be closed without closing the file, I close the connection onClose for that form.
Another thing to keep in mind, is that the computer could be randomly disconnected from the server, so any code that requires the connection should have a quick test to re-open the connection if it got closed by accident somehow.
EDIT:
In it's own module.
Public DB_CONNECTION As ADODB.Connection
Function openConnect(ByRef myconn As ADODB.Connection) As Integer
Static retries As Integer
Dim server As String
server = "localhost"
On Error GoTo connectError
myconn.ConnectionTimeout = 10
myconn.Open "DRIVER={MySQL ODBC 5.1 Driver};SERVER=" & server & "DATABASE=data;USER=" & getSQLuser & ";PASSWORD=password;Option=3"
openConnect = 1
retries = 0
Exit Function
connectError:
'retry several times on failure
Dim errADO As ADODB.Error
For Each errADO In myconn.Errors
Debug.Print vbTab & "Error Number: " & errADO.Number
Debug.Print vbTab & "Error Description: " & errADO.Description
Debug.Print vbTab & "Jet Error Number: " & errADO.SQLState
Debug.Print vbTab & "Native Error Number: " & errADO.NativeError
Debug.Print vbTab & "Source: " & errADO.Source
Debug.Print vbTab & "Help Context: " & errADO.HelpContext
Debug.Print vbTab & "Help File: " & errADO.HelpFile
If errADO.Number = -2147467259 Then
If retries < 3 Then
If MsgBox("Connection Error, Try to reconnect or close any connection-enabled forms,check your internet connection and try again.", vbCritical + vbRetryCancel, "Connection Error") = vbRetry Then
retries = retries + 1
Call openConnect(myconn)
Exit Function
End If
Else
MsgBox "Connection error. Retried 3 times, check your internet connection and/or contact your system administrator.", vbCritical + vbOKOnly, "Critical Connection Error"
retries = 0
Exit Function
End If
End If
Next
Select Case err
Case 0
Case Else
MsgBox "Error Code " & err & ", " & Error(err), vbCritical, "Error #" & err
End Select
openConnect = -1
End Function
Function closeConnect()
If Not (DB_CONNECTION Is Nothing) Then
If DB_CONNECTION.State = adStateOpen Then
DB_CONNECTION.Close
End If
End If
End Function
Function tryConnect()
Dim err
If DB_CONNECTION Is Nothing Then
Set DB_CONNECTION = New ADODB.Connection
Call openConnect(DB_CONNECTION)
Else
If Not (DB_CONNECTION.State = adStateOpen) Then
Call openConnect(DB_CONNECTION)
End If
End If
End Function
In my case, I never call openConnect directly, but always call tryConnect onOpen of any forms that use the DB, or before calls that might happen after some time (for example, the save button). If it's already open, no harm done, but if it's not it prevents an error.
closeConnect I call OnError and OnClose of the menu form.
Related
Access 365/Windows 10
I’m getting the “Could not find installable ISAM” error which I believe means I’ve a problem with my connection string below.
I did a right click, export on a single Access table to the MySQL backend so that I could link it and verify the driver, server, port, database, etc. of that connection against the connection string in the function below. It all looks good. Can you see what I've done wrong?
I have 128 tables to migrate to MySQL and am looking for a efficient, repeatable process; I had high hopes for this code...
'''
Public Function fncExportTables() As Boolean
'Declare Variables...
Dim strCnn As String
Dim rs As Recordset
Dim db As Database
Dim strTp As String
Dim strOriginal As String
'The Connection String required to connect to MySQL.
'I THINK THIS IS THE PROBLEM
strCnn = "DRIVER={MySQL ODBC 8.0 Driver};" & _
"SERVER=myServer;" & _
"PORT=24299;" & _
"DATABASE=myDb;" & _
"USER=myUserName;" & _
"PASSWORD=myPassword;" & _
"OPTION=3;"
strTp = "ODBC Database"
'Trap any Errors...
On Error GoTo Error_fncExportTables
'Open a recordset from the table the conatains
'all the table names we want to Link from the
'MySQL Database.
Set db = CurrentDb
Set rs = db.OpenRecordset("qselMgr", dbOpenSnapshot)
With rs
'Fill the Recordset...
.MoveLast
.MoveFirst
'Enumerate through the Records...
Do Until rs.EOF
'Place the Table Name into the str string variable.
' FieldName (below) would be the Field name in your Access
' Table which holds the name of the MySQL Tables to Link.
strOriginal = !strOriginalName
'Make sure we are not dealing will an empty string..
If Len(strOriginal) > 0 Then
'Link the MySQL Table to this Database.
'ERROR TRIGGERS ON THE LINE BELOW
DoCmd.TransferDatabase acExport, strTp, strCnn, _
acTable, strOriginal, strOriginal
End If
'move to the next record...
.MoveNext
Loop
End With
'We're done...
Exit_fncExportTables:
'Clear Variables and close the db connection.
Set rs = Nothing
If Not db Is Nothing Then db.Close
Set db = Nothing
Exit Function
Error_fncExportTables:
'If there was an error then display the Error Msg.
MsgBox "Export Table Error:" & vbCr & vbCr & _
Err.Number & " - " & Err.Description, _
vbExclamation, "Export Table Error"
Err.Clear
Resume Exit_fncExportTables
End Function
'''
I'm getting the error "Could not update; currently locked." when two or more users insert a record in two different MS Access database files nearly simultaneously. What appears to be happening is that running a second copy of the code causes both databases to eventually become locked at the same time and each program is waiting for the other to release the locks on the table it was inserting into. So the first instance of the program goes into the error trap to wait for Logs.accdb to become unlocked so it can insert into the Logs table and the second instance of the program goes into the error trap to wait for Data.accdb to become unlocked so it can insert into the Transactions table. It is very common for us to have multiple computers running multiple exes. Each exe has two connections, one for each database. This configuration absolutely cannot be changed.
The production code uses substantially more complicated (and better organized) code, but I've reduced it down to the bare minimum to verify that code complexity wasn't the issue. I've created a test program that can recreate the error and have been unable to figure out any way to stop the error from happening other than switching the CursorLocation from adUseServer to adUseClient. This solution is unacceptable though because of the drastic increase in the time it takes to execute queries.
Things I've tried that didn't stop the error:
Closing the connection after every insert. (In addition to not fixing the issue, this also caused drastic increases in the execution time.)
Closing all connections when the error is detected and trying to Resume.
Every permutation of CursorTypes.
Replacing the ADODB RecordSet Update method for SQL Execute (INSERT INTO ...).
Converting the Access 2003 database to Access 2013 and switching Jet 4.0 to Jet Ace 12.
Creating a Front End for the database. (The databases typically reside on a network share with no Front End.)
Moving the databases to C:. (I've read that long paths to the database can generate errors.)
Changing the Client Settings within Access to do Row-based locking and changing All Locks to No Locks to Edited Rows.
Edit: Add a delay in the error trap before Resume.
I'm looking for any help at all at resolving this issue without making drastic changes (like switching to SQL Server, which I've also tried and also resolves the issue but is also not an option). This is in production code for a very long-running company, so major changes are extremely difficult to implement (and also why there's so many 'requirements').
This is the code for the test program. The two WriteRecord functions are practically identical. Compiling this code into an exe and running two copies of the exe on the same machine always recreates the error.
Option Explicit
Public oLConnection As ADODB.Connection
Public oTConnection As ADODB.Connection
Sub Main()
Dim RecordID As Long
On Error GoTo ErrorOccurred
For RecordID = 1 To 100000
DoEvents
Transaction_WriteRecord "Joe", 40000
Log_WriteRecord "Inserted Transaction", "Joe"
Next 'RecordID
ErrorOccurred:
oTConnection.Close
Set oTConnection = Nothing
oLConnection.Close
Set oLConnection = Nothing
End Sub
Public Function Transaction_WriteRecord(TransAccount As String, TransDate As Long) As Boolean
Dim rsTransaction As ADODB.Recordset
Dim sQuery As String
Dim TryCount As Integer
Static RecordID As Long
On Error GoTo ErrorOccurred
sQuery = _
"SELECT * " & _
"FROM Transactions " & _
"WHERE RecordID = 0"
If oTConnection Is Nothing Then
Set oTConnection = New ADODB.Connection
oTConnection.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Data.accdb"
oTConnection.CursorLocation = adUseServer
oTConnection.Open
End If
Set rsTransaction = New ADODB.Recordset
rsTransaction.ActiveConnection = oTConnection
rsTransaction.CursorType = adOpenKeyset
rsTransaction.LockType = adLockOptimistic
rsTransaction.Open sQuery, Options:=adCmdText
rsTransaction.AddNew
rsTransaction!TransAccount = TransAccount
rsTransaction!TransDate = TransDate
rsTransaction.Update
RecordID = rsTransaction!RecordID.Value
rsTransaction.Close
Transaction_WriteRecord = True
Exit Function
ErrorOccurred:
'If the error is that the table is locked because another user
'is adding or deleting records, just try again.
If VBA.Error(Err) Like "Could not update; currently locked*" Then
DoEvents
TryCount = TryCount + 1
If TryCount >= 5 Then
MsgBox "Could not update; currently locked. " & vbCr & _
"Table: Transaction" & vbCr & _
"RecordID: " & RecordID
Err.Raise Err.Number
Else
Resume
End If
Else
Err.Raise Err.Number
End If
Transaction_WriteRecord = False
End Function
Public Function Log_WriteRecord(LogMessage As String, LogAccount As String) As Boolean
Dim rsLog As ADODB.Recordset
Dim sQuery As String
Dim TryCount As Integer
Static RecordID As Long
On Error GoTo ErrorOccurred
sQuery = _
"SELECT * " & _
"FROM Logs " & _
"WHERE RecordID = 0"
If oLConnection Is Nothing Then
Set oLConnection = New ADODB.Connection
oLConnection.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Logs.accdb"
oLConnection.CursorLocation = adUseServer
oLConnection.Open
End If
Set rsLog = New ADODB.Recordset
rsLog.ActiveConnection = oLConnection
rsLog.CursorType = adOpenKeyset
rsLog.LockType = adLockOptimistic
rsLog.Open sQuery, Options:=adCmdText
rsLog.AddNew
rsLog!LogMessage = LogMessage
rsLog!LogAccount = LogAccount
rsLog.Update
RecordID = rsLog!RecordID.Value
rsLog.Close
Log_WriteRecord = True
Exit Function
ErrorOccurred:
'If the error is that the table is locked because another user
'is adding or deleting records, just try again.
If VBA.Error(Err) Like "Could not update; currently locked*" Then
DoEvents
TryCount = TryCount + 1
If TryCount >= 5 Then
MsgBox "Could not update; currently locked. " & vbCr & _
"Table: Logs" & vbCr & _
"RecordID: " & RecordID
Err.Raise Err.Number
Else
Resume
End If
Else
Err.Raise Err.Number
End If
Log_WriteRecord = False
End Function
I am making a application which will verify a login to a MySQL Server, which is being done with an ADODB.Connection, then open up another HTA if no errors are given. I am trying to achieve this using nothing but HTA, VBScript and hard work … Without going into too much detail, I would like to catch an error message which will come up if the user puts in the wrong username/password.
Normally, if you do put in the wrong password or username, it comes up with a HTML Error message, which doesn't look very user friendly. That's what this code is trying to catch and make more friendly. Code segment below:
Sub MainLogin_click
dim strError
Uname = usertext.Value
Pword = passtext.Value
cstring = "DRIVER={MySQL ODBC 5.2w Driver};SERVER=Localhost;UID=" & Uname _
& ";PWD=" & Pword & ";DATABASE=maindb;"
conn = CreateObject("ADODB.Connection")
On Error Resume Next
conn.Open cstring
Call MakeDSN
If Err.Number <> 0 Then
strError = "LongBow Error" & VbCrLf & " Error Number: " & Err.Number _
& VbCrLf & " Error Source: " & Err.Source _
& VbCrLf & " Error Description: " & Err.Description
MsgBox(strError)
Err.Clear
Else
Set objShell = CreateObject("WScript.Shell")
objShell.Run("bin\LongBowAgent.hta")
Window.close
conn.Close
End If
On Error GoTo 0
End Sub
Basically when I run this code, and my HTA as a whole, either
Nothing happens, no activity when the 'login' button is pressed.
or
I get a nice error box (even with the right username/password) saying
Error 424, VBScript runtime error, object Required.
Honestly - I've been at this for 2 days now and tried many things. None have really worked, but then Google Searching can only do so much.
Any Help, and any advise would be great. I do not mind trying something completely different here, so long as it stays within HTA & VBScript.
The scope of your OERN is to large - it does not only hide errors of the .Open, but also problems in MakeDSN and in the error handling code.
Ironically, the first culprit is not covered by the OERN:
conn = CreateObject("ADODB.Connection")
is wrong, because you assign an object. Try
Set conn = CreateObject("ADODB.Connection")
I'm trying to run a tool created in Access through a VB Script, but it needs to receive 7 variables to run. I don't know how to send data to Access for it to use when running the tool, and all the other posts say to connect directly with the database, but the tool is too complex to do that efficiently.
Here's the code so far:
'initialise variables to hold the parameters passed in
Dim v_VBTOOL
Dim v_Procedure
Dim o_accessApp
'Assign the parameters to the variables
v_VBTOOL = WScript.Arguments(0)
v_Macro =WScript.Arguments(1)
Wscript.echo "The Database to open is " & v_VBTOOL
Wscript.echo "The Macro to run is " & v_Macro
'Create an MSAccess application Object
'=================================================
Set o_accessApp = createObject("Access.Application")
'Open the required VBTestTool database
'=================================================
o_accessApp.OpenCurrentDataBase(v_VBTOOL)
'Now run the relevant Macro
'=================================================
o_accessApp.DoCmd.RunMacro(v_Macro)
If Err.Number > 0 Then
' There was an error. Inform the user and halt execution
strMsg = "The following error was encountered while compacting database:"
strMsg = strMsg & vbCrLf & vbCrLf & Err.Description
WScript.Echo strMsg
Else
WScript.Echo "VBTOOL PROCEDURE WAS SUCCESSFUL"
o_accessApp.Quit
Set accessApp = nothing
End If
Any help or tips would be appreciated!
I have linked the sql server tables to ms access so that I can use ms access as the front end.I was able to access the tables from access, until I run into an error ODBC call failed when I tried to open one of the tables. There was no problem with the other tables. Actually I have changed a column name in sql server after creating a link. Is this the problem? I am really worried about this as I was about to use access as a front-end for my future purposes.
When you link to a remote table, Access stores metadata about that table. When you later change the table structure, the metadata doesn't get updated to capture the change.
Delete the link. Then recreate the link. That way the metadata will be consistent with the current version of the table.
Yes changing the column name after linking the table is most likely causing your failure. Is it is now trying to pull data from a column that no longer exists. You will need to relink the table. You can programatically link tables in access. We do that in may of our access applications and drive the tables that need to be linked from a local access table.
Public Sub LinkODBCTables()
Dim objRS As DAO.Recordset
Dim objTblDef As DAO.TableDef
Dim strTableName As String
Dim strAliasName As String
Dim strDSN As String
Dim lngTblCount As Long
Set objRS = CurrentDb.OpenRecordset( _
" select TableName," & _
" AliasName," & _
" DSN," & _
" DatabaseName," & _
" Development_DSN," & _
" UniqueIndexCol" & _
" from tblODBCLinkedTables " & _
" order by TableName", dbOpenSnapshot)
While Not objRS.EOF
' Check to see if we already have this linked tableDef
' We don't care if it is not actually in there
strTableName = objRS.Fields("TableName")
If Not IsNull(objRS.Fields("AliasName")) Then
strAliasName = objRS.Fields("AliasName")
Else
strAliasName = strTableName
End If
If DEV_MODE Then
strDSN = objRS.Fields("Development_DSN")
Else
strDSN = objRS.Fields("DSN")
End If
On Error Resume Next
CurrentDb.TableDefs.Delete strAliasName
If Err.Number <> 0 And _
Err.Number <> 3265 Then ' item not found in collection
Dim objError As Error
MsgBox "Unable to delete table " & strAliasName
MsgBox Err.Description
For Each objError In DBEngine.Errors
MsgBox objError.Description
Next
End If
On Error GoTo 0
Set objTblDef = CurrentDb.CreateTableDef(strAliasName)
objTblDef.Connect = g_strSQLServerConn & _
"DSN=" & strDSN & _
";DATABASE=" & objRS.Fields("DatabaseName") & _
";UID=" & g_strSQLServerUid & _
";PWD=" & g_strSQLServerPwd
objTblDef.SourceTableName = strTableName
On Error Resume Next
CurrentDb.TableDefs.Append objTblDef
If Err.Number <> 0 Then
Dim objErr As DAO.Error
For Each objErr In DBEngine.Errors
MsgBox objErr.Description
Next
End If
On Error GoTo 0
' Attempt to create a uniqe index of the link for updates
' if specified
If Not IsNull(objRS.Fields("UniqueIndexCol")) Then
' Execute DDL to create the new index
CurrentDb.Execute " Create Unique Index uk_" & strAliasName & _
" on " & strAliasName & "(" & objRS.Fields("UniqueIndexCol") & ")"
End If
objRS.MoveNext
Wend
objRS.Close
End Sub
We are using a single SQLServer login for our access applications so the g_strSQLServerUID and g_strSQLServerPwd are globals that contain that info. You may need to tweek that for your own needs or integrated security. We are setting up two DSNs one for production and the other for development. The DEV_MODE global controls wich set of DSNs are linked. You can call this code from a startup macro or startup form. It will deleted the old link and create a new link so you always have the most up to date schema.