Office VBA ODBC to SQL Server - sql-server

I have to connect a Word Document to a SQL Server with VBA in order to read and write records. Now, I'M really confused as of the many different ways to connect to a database. There's ADO, ODBC and so on. I've seen very different ways of connecting a SQL Server database.
A co-worker wants me to use ODBC.
So: What is the best way to connect to a SQL Server database using ODBC in Office VBA?
An example for a SELECT and INSERT statement would also be appreciated.
Edit: Isn't there something like "System.Data.SqlClient.SqlConnection" and "System.Data.SqlClient.SqlCommand" in VB .Net/C#?

This example uses ODBC:
'Microsoft ActiveX Data Objects x.x library
Dim cn As New ADODB.connection
Dim rs As New ADODB.recordset
cn.Open "DRIVER={SQL Server};SERVER=srv;" & _
"trusted_connection=yes;DATABASE=test"
rs.Open "SELECT * FROM table_1", cn
This example uses OleDb
'Microsoft ActiveX Data Objects x.x library
Dim cn As New ADODB.connection
Dim rs As New ADODB.recordset
cn.Open "Provider=sqloledb;Data Source=srv;" & _
"Initial Catalog=Test;Integrated Security=SSPI;"
rs.Open "SELECT * FROM table_1", cn
See also: http://www.connectionstrings.com/sql-server-2008

The reason your co-worker probably wants you to use ODBC is that your company delivers standardized ODBC connections for every single workstation.
The good thing here is that you don't have to worry about connection properties except login/password, because it's all set. Imagine ODBC acting between a software (word) and a server, it makes things alot easier when it comes to changes in your company. (Server address may change and IT is able to change settings of ODBC without the end user even knowing about it)
Beste would be to talk to your co-worker and ask about his concerns. Another reason could be that ODBC might be faster than the ado one. I made this experience, too.

Related

Error when opening ADODB recordset using odbc dsn in MS Access

I have a MS Access application that has been in use for at least 10 years. Recently I moved my work to a new development machine with Office 365 and SQL Server Express 2019 installed. The machine OS is Windows 10 Pro. In my old machine my application would run just fine. There is a query using a stored procedure that retrieves a piece of data from a table in the SQL Server backend. I call this procedure using a ADODB recordset based on the stored procedure output. All my calls use a connection string based on an ODBC DNS. This connection string works fine on the new machine when relinking tables to the SQL Server backend, but when I use it in the ADODB connection I get an error "[Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified". On the old Windows 7 development machine this ran fine. It also runs on the client's machines which are Windows 10 and Office 365. The code that raises the error follows. The error occurs at the open connection line. The connection string is: ODBC;DSN=VROM;Trusted_Connection=Yes;APP=2019 Microsoft Office system;DATABASE=VROM1.
Set con = New ADODB.Connection
con.ConnectionString = DLookup("Link", "tblLinkData", "Use = True")
con.Open
Set rs = New ADODB.Recordset
My question "Is there a setting in SQL Server that could be causing this error?". As far as I can tell both systems are set up the same, but there is obviously something different between the two. Is there another reason this could be working on one system and not another?
Hum, where to start?
Ok, first up, if you using the ODBC panel to create a ODBC connection, then you NEVER were actually using a ADO connection in that DSN config = ODBC!!!!
That means you been feeing ADO a ODBC connection string. you can do this, but at least lets be aware of what is going on.
Next up:
Are you running the SQL browser service? You MUST do that now!. Don't know if you installed a later version of SQL SERVER, but a few versions ago you will find now that you MUST run the browser service.
This one:
The reason of course is that you can (may) have multiple instances of sql server running, and the default instance (SQLEXPRESS) now MUST BE specified. As noted older instances did NOT have this requirement. And to "resolve" the multiple instances, you now must run the SQL browser service. As noted, this was NOT a requirement in the past - it is now. That browser service is what connects the IP/server name to the instance, and you now in most cases have to run this.
There are exceptions, but you don't want much pain.
Next up:
For a VERY long time, it is recommended for Access linked tables you use a DSN-less connection. that way then at deployment time to all workstations, you don't need to setup a DSN on each workstation.
And if you link Access tables using a FILE dsn (not system or user), then upon the table link process, access BY DEFAULT converts all table links to DSN-less. That means once you linked, then you could even delete the DSN - you do NOT need it anymore.
Again: this ONLY holds true if and when you create a FILE dsn, and use that to link the tables. So, that takes care of the DSN-less linked tables.
Note that if you were to modify the DSN, you would have to link, since I just told you by default access creates + uses dsn-less links (assuming you used a FILE dsn to link).
now, you CAN of course use that SAME dsn for ADO, but that means you are feeding ADO a ODBC driver connection!!!
If you want to create a real ADO connection, then you would/could/should say use this:
Dim strCon As String
strCon = "Provider=SQLOLEDB;;Initial Catalog=TEST4; " & _
"Data Source =.\SQLEXPRESS;Trusted_Connection=yes"
Dim conn As New ADODB.Connection
conn.ConnectionString = strCon
conn.Open
Dim rst As New ADODB.Recordset
rst.Open "SELECT * from tblHotels", conn
Do While rst.EOF = False
Debug.Print rst!HotelName
rst.MoveNext
Loop
Now now how I used a "." for localhost. That could be replaced with (local), or in fact the computer (server) name say like this:
strCon = "Provider=SQLOLEDB;;Initial Catalog=TEST4; " & _
"Data Source =ALBERTKALLAL-PC\SQLEXPRESS;Trusted_Connection=yes"
Next up, make sure you enabled the TC/IP connections.
here:
However, since your case all along you actually been using ODBC driver with ADO, then I would not rock the boat.
But, check your settings as per above.
Since linked tables, and quite much everything else is DAO + odbc?
then I would probably not introduce ADO into that application for JUST calling + using sql server store procedures.
I as a general rule would say use this;
with currentdb.tabledefs("qryPT")
.sql = "exec MyProcName"
.execute
end with
Or, if it is to return data, then I would use this:
dim rst as DAO.RecordSet
with currentdb.tabledefs("qryPT")
.sql = "exec MyProcName"
set rst = .OpenRecordSet()
end with
So, I am hard pressed to find a reason to use ADO. Now of course since you been using ADO, then I guess I would continue to do so.
but, check the browser settings. And if the tables were linked using a FILE dsn, then they are dsn-less.
You could try feeding the ADO connection a existing connection from a existing linked table.
eg:
strCon = CurrentDb.TableDefs("dbo_tblHotels").Connect
strCon = Mid(strCon, 6)
Dim conn As New ADODB.Connection
conn.ConnectionString = strCon
conn.Open
Skipping the first 6 chars skips this part:
ODBC;DRIVER=SQL Server;SERVER=ALBERTKALLAL-PC\SQLEXPRESS;
UID=AlbertKallal;Trusted_Connection=Yes;APP=Microsoft Office 2010;
DATABASE=Test4;Network=DBMSLPCNM
So once again we are feeding ADO ODBC connection string.
(of course above connection string is one line).

VBA Connecting to SQL Server 2017 - Error(Unable to complete login process due to delay in opening server connection)

WE use a spreadsheet to collect and distribute various data into sheets for company use.
Use were using MS Excel 2013 and an early version of MS SQL Server
Which worked perfectly without any errors, here is the code from our VBA file:
Dim con As ADODB.Connection
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
Set cmd = New ADODB.Command
Set rs = New ADODB.Recordset
MyConStr = "Provider=SQLOLEDB;Data Source=sqlserver\SQL;Initial Catalog=MYDB;user id=sa;password=Mon87653day9856"
con.Open MyConStr
Now we have upgraded to SQL Server 2017, and we get the error.
Unable to complete login process due to delay in opening server connection
I have tried looking at pipes and changing the order, still get the error.
I have downloaded the new MSOLEDBSQL driver and still get the error.
I have tried the trusted connection and time out settings and still get the error.
Any one got any ideas?
Edited
I am not sure if this was a mistake in your posting or in your original code, but you are missing the terminating quotation marks at the end of your connection string (I would put this in the comments but don't have a high enough reputation).
Also, in the past, I would have to specify the IP address of the server in order to establish the connection to the server. May be worth a try.

Generic Windows Account To Query Data

I have been able to run the following VBA code (within Excel) using my own Windows account to pull data from a SQL server:
Sub GetData()
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset
Dim ConnectionString As String
ConnectionString = "Provider=SQLOLEDB; Data Source = ServerName;Trusted_connection=yes;"
However, now I'm at the point where I have additional users I would like to provide the file so that they can run the VBA and refresh the data (among other things). I have a generic Windows account that I would like to use, however the solution is not as simple as changing the connection string as shown below because the password is not a SQL server password but rather a Windows password.
ConnectionString = "Provider=SQLOLEDB; Data Source = ServerName;UID=user;PWD=password"
I've read about things such as "impersonation", but haven't been able to figure it out. Is there any reasonably simple solution to make this work? Much thanks!

Connecting a Multi User Access Front End to SQL Server Back End

Thank you for your help as always!
I have built an Access database that we intend to distribute to ~100 end users. I am currently running a pilot program for 5 users. I have migrated the tables to SQL 2014 Server but currently the back end of the pilot program is in a shared network folder (location unknown to them). I distributed the Access front end (accde file type) by saving the file on that shared Network and instructing the end users to save a copy to their desktop. The pilot is working fine, if not a little slow.
The issue that I am having is this:
I am able to set up an ODBC Connection on my machine and am able to connect to the SQL Server backend through that connection. However, I am not sure how to get my end users Access to the tables on the server. Do I need to create a user name for each user on the server and give them read and write access? Do I also need to create an ODBC connection on each machine that we plan to install the Front End on? I would prefer to not have to create an ODBC connection on each machine, is it possible to work around this? Thank you!
Access Version: 2013
SQL: 2014
Working on SSMS 2014
-Charlie
A few things:
When you create a linked table, simply use the ribbon import and link – and then ODBC database. Just choose a file DSN. The reason for this is that access by DEFAULT will use a DSN-less connection. In simple terms, this means when you link the tables, then you can distribute your applcatation to each workstation and there no need to setup a SYSTEM/Machine DSN.
So just keep in mind that use the default file DSN – once Access creates the link to SQL server, then such links are DSN-less, and you don’t need any setup on each workstation.
As for creating users on SQL server? Well, you likely don’t need to unless you want some kind of special security for each user. If you using SQL logon, then MAKE sure that during the above linking process you “check” the save password option. Once again, since by default the linked tables are DSN-less, then every user will in fact be using the one and same SQL User/password and thus this will be transparent to each user (they will not have to log on).
If you using windows authentication for SQL logons, then security is setup with the windows system and not SQL server. In this case then each users windows logon will be used to control (permit) use of the SQL server. If you not using a domain controller, then you be using SQL logons, and likely just the one logon that you are using will suffice. Often even in a corporate environment because I don’t want to call up the IT admin folks for each logon and permissions to SQL server, then I still OFTEN choose SQL logons. Thus “once” the IT admin folks give me enough rights to the SQL server, then I am free to create my own logons, or just use the “one same” logon for everyone and thus don’t have to waste time bothering the IT folks.
A few additional final points:
Ignore suggesting to use all kinds of ADO and VBA code and connection strings etc. – they are not required. In fact in most cases you want to AVOID ADO code in your application. And oleDB is being depreciated for SQL server (which ADO tends to rely on).
You STILL as a matter of good deployment want to place the front end program you have on each workstation. Just like you install word on each workstation, or your accounting packages, now that YOU ARE developing software, then you install your software on each workstation like the IT industry done for the last 30 years. You can certainly share data on a shared folder, but you install the actual application (word, Excel, or in this case YOUR applcatation on EACH workstation. And you should compile the accDB to an accDE before any deployment.
So you don’t really need any special code on start up to “connect” or “link” to SQL server if your deployment to such users is on your same network. If you are developer or consultant “offsite”, then you likely need to add some code on start up to re-link to THEIR sql server that no doubt would be different than the one your developing with offsite. So some ability to re-link to a “different” SQL server then the one your are developing with would be required if you not able to develop on site, or that the SQL server you working with is a “copy” or “test” version of the Actual production SQL server being used.
You do not need an ODBC connection on every machine. You can create the connection by using ODBC connection strings via DoCmd.TransferDatabase. This is persistent, so you can just execute the connection/updates on your development copy of the front end and the connection will exist and persist in any end user copies of that that you make.
You do have to deal with the authentication issue. Depending on your security situation you can create a single "database user" and include the username and password to the above connection. If you are in a AD environment you can use that for authentication. Or you create individual SQL accounts for each user or user group. No matter what you do the users will be able to see and edit the tables if Access is to have read/write permission to them. You can secure individual tables by using views to link them to Access instead of the tables themselves.
I would advise against having users make a local copy of the front end. That makes it a nightmare to make sure your users are using the latest version of your front end. Encourage them to make a shortcut instead. Unless you have per-user local temp tables or code that dynamically edits forms/reports there is not much reason not to have everybody using the same front end file. Even in that case create a deployment batch file that creates/overwrites the front end copy for each user.
Sample code that links to SQL server database:
Public Sub LoadDatabase()
Dim MyDB As DAO.Database
Set MyDB = CurrentDb
Dim d As DAO.Recordset
Set d = MyDB.OpenRecordset("SELECT TableName FROM tblLinkTables")
d.MoveFirst
Do While Not d.EOF
DoCmd.DeleteObject acTable, d!TableName
DoCmd.TransferDatabase acLink, "ODBC Database", "ODBC;Driver={SQL Server};Server=MYSQLSERVERNAME;Database=MyDatabaseName;Uid=DatabaseUser;Pwd=DatabaseUserPassword", acTable, d!TableName, d!TableName, False, True
d.MoveNext
Loop
d.Close
Set d = Nothing
End Sub
where the list of tables to link exists on a local table called tblLinkTables with a single field TableName for each table to be linked. This is a very simplified version. The one I actually use has the capability to have different local table names than the SQL tables and also link views and create primary keys (optionally) for them.
The following is one example of how you can obtain a connection to SQL Server. It supports using either a Trusted Connection (where the windows user is authorized access, or you can specify a sqlserver defined userid and password.
' The following are some of the parameters the connection will use.
Global gv_DBS_SQLServer As ADODB.Connection
Global gvstr_SQLServer_Name As String
Global gv_SQLServer_DSN As String
Global gvstr_SQLServer_Database As String
'Call the GetConnection Function
' Pass Name of Server; Database Name; Connection Variable; adUseServer; True if using Password; False if not using a Trusted Connection;
' Sample
If GetConnection(gvstr_SQLServer_Name, gvstr_SQLServer_Database, _
gv_DBS_SQLServer, adUseServer, True, False) = False Then
MsgBox "Unable to connect to SQL Server", vbOKOnly, "No Connection"
End If
Public Function GetConnection(ByVal strDSN As String, _
ByVal strDatabase As String, _
ByRef cnLocal As ADODB.Connection, _
ByVal CursorLoc As CursorLocationEnum, _
ByVal UsePassword As Boolean, _
ByVal blnTrusted As Boolean) As Boolean
Dim strConnectString As String
Dim strDisplay As String
On Error GoTo ERROR_HANDLER
GetConnection = False
Retry_Connection:
If cnLocal Is Nothing Then Set cnLocal = New ADODB.Connection
If cnLocal.State = adStateOpen Then
Debug.Print "Connection already open -- -will not reopen!!"
GetConnection = True
GoTo Proc_Exit
End If
With cnLocal
Debug.Print "Use TRUSTED CONNECTION (ABOVE)"
If blnTrusted = True Then
strConnectString = "Driver={SQL Server};" & _
"Server=" & strDSN & ";" & _
"Database=" & strDatabase & ";" & _
"Trusted_Connection=yes"
Else
strConnectString = "Driver={SQL Server};" & _
"Server=" & strDSN & ";" & _
"Database=" & strDatabase & ";" & _
"User Id=UUUUUUU;Password=" & DecryptString("PPPPPPPP") & ""
strDisplay = "Driver={SQL Server};" & _
"Server=" & strDSN & ";" & _
"Database=" & strDatabase & ";" & _
"User Id=UUUUUU;Password=PPPPPPP"
End If
Debug.Print "Will use Conn String: " & strDisplay
.ConnectionString = strConnectString
.CursorLocation = CursorLoc
.Open
End With
GetConnection = True
Proc_Exit:
Exit Function
ERROR_HANDLER:
Debug.Print Err.Number & vbCrLf & Err.Description
Err.Source = "Module_Connect: "
DocAndShowError
Resume Proc_Exit
Resume Next
Resume
End Function

How to access SQL Server from VBA in a non-deprecated way?

It appears that all ways to directly access an SQL Server database from a VBA project have been deprecated:
DAO with ODBCDirect: Support has been dropped with Access 2007.
DAO via JET: You're not serious, right? Anyway, it's considered obsolete by Microsoft.
ADO with the SQLOLEDB provider: Deprecated.
ADO with the SQL Server Native OLEDB provider: Won't be supported after SQL Sever 2012.
ADO with the Microsoft OLE DB provider for ODBC: Not supported: "SQL Server Native Client is not supported from the Microsoft OLE DB provider for ODBC (MSDASQL)."
What did I miss? What is the official, Microsoft-approved way to access an SQL Server database from VBA (which is, after all, not deprecated and still the official development language included with Office 2013)?
What did I miss?
Plain old ODBC. In VBA projects for Office applications other than Access, ODBC via ADO is the most straightforward:
Sub AdoOdbcExample()
Dim con As Object
Set con = CreateObject("ADODB.Connection")
con.Open _
"Driver={SQL Server Native Client 11.0};" & _
"Server=.\SQLEXPRESS;" & _
"Database=myDb;" & _
"Trusted_Connection=yes;"
con.Execute "UPDATE Clients SET FirstName='Gord' WHERE ID=5;"
con.Close
Set con = Nothing
End Sub
For VBA projects in Access, we also have the option to use ODBC linked tables and pass-through queries via ACE DAO like we always have
Sub DaoOdbcExample()
Dim cdb As DAO.Database, qdf As DAO.QueryDef
Set cdb = CurrentDb
Set qdf = cdb.CreateQueryDef("")
qdf.Connect = "ODBC;" & _
"Driver={SQL Server Native Client 11.0};" & _
"Server=.\SQLEXPRESS;" & _
"Database=myDb;" & _
"Trusted_Connection=yes;"
qdf.sql = "UPDATE Clients SET FirstName='Gord' WHERE ID=5;"
qdf.ReturnsRecords = False
qdf.Execute dbFailOnError
Set qdf = Nothing
Set cdb = Nothing
End Sub
Notes:
SQL Server Native Client 11.0 is the version that ships with SQL Server 2014 (ref: here).
The cited list of Obsolete Data Access Technologies says "DAO 3.6 is the final version of this technology. It will not be available on the 64-bit Windows operating system.". That refers to Jet DAO ("Microsoft DAO 3.6 Object Library"). ACE DAO ("Microsoft Office 14.0 Access database engine Object Library") is indeed available to 64-bit applications if the 64-bit version of the Access Database Engine is installed.
The correct and future way is to use the ACE object model. You are 100% correct that native oleDB is being dropped from SQL server. It ALSO very important to note that the “general” developer community started dropping ADO when .net came out (the ado.net provider is a VERY different beast and one not reliant on oleDB, but the sqlprovider).
So because of this, significant trends are occurring in our industry.
We are moving away from oleDB. This is in general a Windows-only technology. With the rise of iPads, smartphones, Android etc, then you don’t have such platform specific providers and they don't have oleDB. You thus have to revert back TOWARDS using Open Database Connectivity standards (ODBC). Oracle, Microsoft, MySQL have all stated this is the future road and choice.
While JET is considered obsolete, ACE is not.
SINCE Access 2007 (that is now fully 3 versions), you DO NOT and SHOULD NOT have a reference to DAO. So for the last 3 versions of Access you don’t need nor want or use a reference to the DAO object library.
You now should use the new built in ACE database engine. This means you do NOT need a separate reference to DAO.
There are several advantages to the ACE engine:
You don’t need a DAO reference anymore.
Once reference to the data engine takes care of previous two library references.
There is both an x32 and x64 bit edition available (so .net applications etc. can use an x64 bit edition of this data engine). JET was x32 bit only.
The ACE provider continues to receive updates and enhancements. The same cannot be said for JET or in fact much for ADO either.
ACE now supports store procedures and table triggers. It also has support for SharePoint lists which is web services based.
Also changes were made to Access/ACE to work with SQL Azure.
For using Access with SQL server, you simply use ACE and linked tables. As noted the trend away from ADO MUCH started about 13 years ago when .net came on the scene.
Thus the standard approach and recommend now is ACE + odbc.
So you not missed anything here. The confusion stems much from article that state JET is deprecated but THEN leaves out the VERY important detail that Access for THE LAST 3 versions now does NOT use JET, but uses a new library called ACE.
It is SIGNIFICANT that you don’t need nor want a reference to DAO in your Access applications anymore.
You are certainly using a compatible DAO library, and it still even recommend that you prefix your reocrdset code with DAO (so older existing code will work just fine when if you done this in the past, or you always left out the DAO qualifier when declaring recordsets.
And for things like sql passthrough, you simple can use a saved passthrough query, and do this:
CurrentDb.QueryDefs("MyPass").Execute
or how about some t-sql, you can do this:
With CurrentDb.QueryDefs("MyPass")
.SQL = "ALTER TABLE Contacts ADD MiddleName nvarchar(50) NULL"
.Execute
End If
or call a store procedure of your choice "on the fly" with a parameter
With CurrentDb.QueryDefs("MyPass")
.SQL = "Exec MyStoreProc " & strMyParm1
.Execute
End If
Are not the above so nice and clean? As noted the above code examples tend to be FAR less code and hassle then using oleDB/ADO examples posted.
For the long time users of Access that developed their skills around ODBC and sql server, you don’t have to do anything as the industry much decided what you been doing all along is the recommend approach.
While JET-DIRECT is not supported in ACE, I cannot think of any case when this choice is missed by using the passthrough querydef examples as per above in place of JET direct.
When initializing an adodb.connection in vba we replaced
.Provider = "sqloledb"
.Properties("Data Source").Value = sServer
.Properties("Initial Catalog").Value = sDB
.Properties("Integrated Security").Value = "SSPI"
with
.ConnectionString = _
"DRIVER={ODBC Driver 11 for SQL Server}; " & _
"SERVER=" & sServer & "; " & _
"Trusted_Connection=Yes; " & _
"DATABASE=" & sDB & "; "
That uses .Provider = "MSDASQL.1" but you don't have to add that.

Resources