Excel VBA SQL Server connection timeout - sql-server

I'm going to connect to a SQL Server database from different networks (the server has two network cards) and depending on which network my computer is located in, I need to send a message to the user if the database is unreachable from that network (The user checks a checkbox telling the code which network he/she is on).
I want to set the connection timeout to something less than the default (30 seconds), so that the user does not have to wait the whole timeout period. My problem is that all things I have tried don't work.
This is my rudimentary code:
Public Function OpenDatabaseConnection() As Boolean
Set rs = New ADODB.Recordset
Set Cn = New ADODB.Connection
Cn.ConnectionTimeout = 2
Cn.CommandTimeout = 2
On Error GoTo DBError
If gboolNetworSelectionLocal = True Then
Cn.Open gstrCONNECTION_STRING_LOCAL
Else
Cn.Open gstrCONNECTION_STRING_PUBLIC
End If
rs.Open SQLStr, Cn, adOpenStatic
OpenDatabaseConnection = True
Exit Function
DBError:
MsgBox "Database connection attempt failed. Contact your system administrator!", vbExclamation
OpenDatabaseConnection = False
End Function
If anyone has any tips - they are more than welcome!
Thanks!

Related

how to connect VBScript (Excel) to SQL Server web database (via IP)

I'm at my job trying to do some unknow stuff for me, you see, we're trying to connect an excel document with a VBScript Macro to a databse stored in web server but for some reason doesn't recognizes the user and throws an error repeatedly, i discarded a connection issue since it returns an SQL error instead of something like a timeout or server doesn't exists or something like that, we're trying to connect to the server using the ip address, we also checked that the logging method is on mixed (win and sql) and remotes connections to the server are enabled as well, also if i use the credentials provided in the connection string (username and password) i can actually log in to SQL Server without any issue, we also tried a direct connection (external vpn) because we thought it could be our firewall, but got the same error anyway, so we have no clue what it could be and we're kinda running out of ideas on how to do this, i'll post down below the code i'm using to trying the connection (obviously test data but similar to reality)
picture of the error i'm getting (don't post the original since it's in spanish but is very similar to this):
code i'm currently trying:
Sub excel_sqlsrv()
Set rs = CreateObject("ADODB.Recordset")
Set conn = CreateObject("ADODB.Connection")
strConn = "Driver={ODBC Driver 17 for SQL Server};Server=10.20.30.5;Database=mydb;UID=sa;PWD=abcd12345;"
conn.Open strConn
strSqL = "SELECT * FROM USERS"
rs.Open strSqL
End Sub
Any advice, tip or trick could be of tremendous help for me, i'll be looking forward to any kind of comment, thanks in advance
Use the ODBC Data Source Administrator to create a connection named mydb and test it works. Then use
Sub excel_sqlsrv()
Const strConn = "mydb" ' ODBC source
Const strsql = "SELECT * FROM USERS"
Dim conn As Object, rs As Object
Set rs = CreateObject("ADODB.Recordset")
Set conn = CreateObject("ADODB.Connection")
On Error Resume Next
conn.Open strConn
If conn.Errors.Count > 0 Then
Dim i, s
For i = 0 To conn.Errors.Count - 1
s = s & conn.Errors(i) & vbLf
Next
MsgBox s
Else
On Error GoTo 0
Set rs = conn.Execute(strsql)
Sheet1.Range("A1").CopyFromRecordset rs
End If
End Sub
You can try using OLEDB provider instead of ADODB.

Executing stored procedure in MS Access through ADO connection - removing application role

I have an app developed in MS Access which uses ADO connections to SQL server (Microsoft SQL Server 2017) to execute numerous stored procedures. The ADO connections are all done through an application role in order to limit permissions.
In the latest update, I created a few new stored procedures which return several recordsets, which are pasted in Excel. My issue is that when I execute these stored procedures, the application role is removed... The query runs without any issues, but when it finishes running, the application role is unset.
The following is an example of one of the stored procs being called in VBA Access:
Public Function CDTExceptionsReport() As ADODB.Recordset
On Error GoTo ErrorHandler
Set objConn = DB.MaintainConnection
On Error GoTo 0
If objCmd_ER Is Nothing Then
Set objCmd_ER = New ADODB.Command
With objCmd_ER
.CommandType = adCmdText
.CommandTimeout = 60 ' increase command
.CommandText = "EXEC tool.ExceptionsReport;"
.Prepared = True
' set connection object
.ActiveConnection = objConn
End With
End If
Set CDTExceptionsReport = objCmd_ER.Execute
On Error GoTo 0
Exit Function
ErrorHandler:
MsgBox "Error: " & Err.DESCRIPTION & vbNewLine & "Number: " & Err.Number
On Error GoTo 0 ' reset error handling
End Function
Note that objConn is my connection object, and objCmd_ER is my global command object.
Through the immediate terminal in VBA, I can check what role is being activated by using the following in debug mode:
Set RS = objConn.Execute("SELECT CURRENT_USER")
?RS.Fields(0)
If I run this before the objCmd_ER.Execute line, I can see the application role is still in use. However, when I run this immediately after that statement, the application role is removed and my windows username is returned. Has anyone experienced this before?
I've executed this stored procedure in SQL Server directly, and it works fine and does not log the application role out. Therefore my thinking is it's something to do with the ADO connection.
Please let me know what further details would be helpful to provide. The stored procedure does not contain any DDL or DML language - just 4 select queries.
Thanks
Never do this!
.ActiveConnection = objConn
That casts the connection object objConn to a connection string, then creates a new connection using that connection string, and uses that as the active connection.
Instead, always do this:
Set .ActiveConnection = objConn
That actually sets the active connection to your connection object.

VBA test connection to SQL Server

We have multiple SQL servers that our front end connects to. We need a test to check which servers are offline at any one time.
The code below works completely fine with an online SQL server but when testing an offline one takes over 30 seconds to timeout. We need this to timeout much quicker as this is a check that will be run regularly
Function ConTEST()
Dim conn As ADODB.Connection
Set conn = New ADODB.Connection
Dim strConnect As String
strConnect = "DRIVER={SQL Server};SERVER=SERVER;UID=UID;PWD=PWD;APP=Microsoft® Windows® Operating System;DATABASE=DB_NAME"
With conn
.ConnectionString = strConnect
.CommandTimeout = 5
.ConnectionTimeout = 5
.Open
End With
End Function
Any suggestions on how to fix this timeout issue would be greatly appreciated.
if it's just a database on the server that is taken offline, then connect to the "master" database in your connection, and run the "DATABASEPROPERTYEX" function to get the status of the database you want to check.
Dim rs As New ADODB.Recordset
With conn
.ConnectionString = strConnect
.CommandTimeout = 5
.ConnectionTimeout = 5
.Open
Set rs = .Execute("select DATABASEPROPERTYEX('yourdb', 'Status') as db_status")
Debug.Print rs(0).Value
.Close
End With
if the entire server is taken offline, you may want to ping the server instead from the command line.

Keeping UID and PWD out of an ADO connection string in an ODBC DSN-less Database and a DAO cached connection?

I have used Ben Clothier's suggestion from his Office Blog Power Tip (http://blogs.office.com/2011/04/08/power-tip-improve-the-security-of-database-connections/) to create a DSN-less connection with cached credentials so that users' UID and PWD aren't saved, or required multiple times, when working in the Access interface. Have others done this? If so, what has been your approach when you need to use an ADO connection instead of DOA to reach SQL from Access via VBA? How do you open a adodb connection without having to provide the User ID and Password again, or having to put it in the code?
(I'm using Access 2013 frontend, SQL 2008 R2 backend, SQL Server Security)
Thanks in advance!
My Cached Connection code works like this:
Public Function InitConnect(strUserName As String, strPassword As String) As Boolean
' Description: Is called in the application’s startup
' to ensure that Access has a cached connection
' for all other ODBC objects’ use.
Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim rst As DAO.Recordset
Dim strConnection As String
strConnection = "ODBC;DRIVER=sql server;" & _
"SERVER=******;" & _
"APP=Microsoft Office 2010;" & _
"DATABASE=******;" & _
"Network=DBMSSOCN;"
Set dbs = DBEngine(0)(0)
Set qdf = dbs.CreateQueryDef("")
With qdf
.Connect = strConnection & _
"UID=" & strUserName & ";" & _
"PWD=" & strPassword & ";"
.SQL = "Select Current_User;"
Set rst = qdf.OpenRecordset(dbOpenSnapshot, dbSQLPassThrough)
End With
InitConnect = True
ExitProcedure:
On Error Resume Next
Set rst = Nothing
Set qdf = Nothing
Set dbs = Nothing
Exit Function
End Function
Then when I need to access data I can do this (Note the UID and PWD are not required):
Dim dbs As DAO.Database
Set dbs = OpenDatabase("", False, False, "ODBC;DRIVER=sql server;SERVER=*****;APP=Microsoft Office 2010;DATABASE=*****;Network=DBMSSOCN")
I can also set the ODBC connection to pass-through queries as well in Access or VBA. But these connections work only when the connection string is IDENTICAL to what was originally used in my Cached Connection code. So, when I need an ADODB connection (as it seems sometimes ADO is needed?), the string obviously isn't going to be identical.
For Example:
Dim cn As New ADODB.Connection
cn.Open "Provider = sqloledb;Data Source=*same as "SERVER"*;Initial Catalog=*same as "DATABASE"*;User Id=****;Password=****"
This type of connection only works if I supply a User Id and Password. How can I write it so that I don't need them? ~Thanks!
Although ACCESS has some weak points regarding security, you can do few things to minimize the risks. One of them would be compile the DB to ACCDE. This way VBA is compiled and not visible.
You can create a public function that returns a string
Public Function GET_CONNECTION_STRING() as STRING
' construct your connection string here with server name and password
GET_CONNECTION_STRING = "DRIVER={" & Driver & "};PORT=" & mPort & ";DATABASE=" & mDatabase & ";SERVER={" & mServer & "};UID=" & mUser & ";PWD={" & mPassword & "};"
End Function
then create an AutoExe macro that runs when the application is opened.
in your AutoExe perform refreshing links to your linked tables. something similar to what you have.
For Each tdf In db.TableDefs
If tdf.connect <> vbNullString Then
tdf.connect = GET_CONNECTION_STRING & ";TABLE=" & tdf.name
tdf.RefreshLink
End If
Next tdf
you can do the same for existing pass through queries:
For Each myQuerydef In MyDB.QueryDefs
If Left(myQuerydef.connect, 4) = "ODBC" Then
myQuerydef.connect = "ODBC;" & GET_CONNECTION_STRING
myQuerydef.Close
End If
Next
in addition you can have some other public functions to get current logged in username.
something like
public function getCrruserID() as int
'check your public variable crr_user_id if its empty redirect to login
if nz(crr_user_id,0) = 0 then
'go to login and save the user id after successful login
else
getCrruserID = crr_user_id
end if
end function
use simple DAO to execute sql code like
dim db as DAO.Database
set db = currentdb
dim rs as Dao.Recordset
set rs = db.openrecordset("select something from your linked table")
or
db.execute "update command", dbfailonerror
to your last question. if you save something in memory it will be destroyed once your application is closed.
EDIT:
if you have more than 50 linked tables it might be not a good idea to refresh them at every startup. Instead you can create a Local table containing your [local_Appversion, isFreshInstall] and some other variables as per your need. Every time your user receives an update the freshInstall will be true and code your App to connect and refresh all tables. (just to make sure client will get uninterrupted connection)
so in your autoExe code: if its freshInstall then
connect and refreshlinks if not just set the connectionString. (usually a splash screen after login to perform this action)
After successful connection just update the local isFreshInstall value to false for a quicker start next time.
you can also have a dedicated menu where user can click and refresh links manually.(in case if the connection get dropped)
something like
if your organisation has a domain you can allow trusted connection using windows login name
good luck.

Connection to sql server database lost in a vb6 app after computer going to sleep

I have an old visual basic 6 application when some users reported me errors when the computer going back from sleep. This problem did not occurs on every client computer, (I would say some Windows 7). If the vb6 application was still open then if they try to use this application it crashes with the following error message.
I debugged and I found the problem: I have a global variable that keep the connection to the database. This variable is initialized only once in the beginning of the application. When the computer going to sleep and go back some times later, the status of this variable is still "OPEN" but in fact the connection is lost! If I "CLOSE" and then "OPEN" this variable connection I am able to query the database.
I wonder if this is normal that I lost my database connection?!
Here is some code:
' This is my global variable
Global cn As New ADODB.Connection
' Set connection properties for sql server.
cn.ConnectionTimeout = 25
cn.Provider = "sqloledb"
cn.Properties("Data Source").Value = ".\SQL2008"
cn.Properties("Initial Catalog").Value = DB_INITIAL_CATALOG
cn.Properties("User ID").Value = DB_USERNAME
cn.Properties("Password").Value = DB_PASSWORD
cn.Open
' This is a typical query on my database
Set rs = New ADODB.Recordset
strSql = "SELECT * FROM tblUsers"
rs.Open strSql, cn, adOpenKeyset
Any idea?
Thanks.
Yes, I've seen this kind of thing before. There may be settings on the connect string that can help reduce it, but time outs and/or network drops can break the underlying (often TCP) connection to the DB server. You then see the error manifest itself in the next I/O to the database.
I recommend wrapping access to the shared connection so you can transparently catch that specific error and retry. Keep the connection private in a class or module and have methods such as:
'Open is called to set the args to connect, these should be saved for reconnect
Public Sub Open(connect params here)
'save arsg to prive members to reconnect
'connect to db
End Sub
Public Function OpenKeyset(sql) As RecordSet
Set rs = New ADODB.Recordset
On Error Resume Next
rs.Open strSql, privateConn, adOpenKeyset
'if the error is the disconnect
If Error.Number = xxx Then 'or inspect the error message or error collection
'turn of error trap
Err.Clear
On Error Goto 0
'reopen db conn
'then retry
rs.Open strSql, privateConn, adOpenKeyset
End If
OpenKeyset = rs
End Function
You could even periodically perform a no-op db operation like quering the catalog or whatever to keep the connection live and proactivle reconnect. You couild also watch for large jumps in time that happen if a computer hibernates.
You should trap the sleep and wake events in your vb6 code, any long running queries should be closed at sleep, so that the dB connection can be closed. At wake, you do the opposite. You need to listen to
WM_POWERBROADCAST message.https://msdn.microsoft.com/en-us/library/windows/desktop/aa373247(v=vs.85).aspx
Good luck
Jeppe

Resources