If you’ve ever asked Access to do a join between a local Access table and SQL Server table (which is called a heterogeneous join) you may have experienced first hand how slow it can be to process results. The situation could be vastly superior if you can afford to download the SQL table as a temporary table to your Access front-end and then process the join. This post will provide you with an easy solution to download SQL Server data into Access using a subroutine you can call from code.
Pass Through Query + Make Table Query Combo (No Linked Table Approach)
There is no direct way to download SQL Server data into Access using code, you can’t use a ADODB recordset or command to download the data, since they only “see” the SQL Server side and don’t have exposure to your Access data. The technique I’m providing here will use a temporary pass-through query, then using it with a make table query to create the local Access table copy of your SQL data.
'Insert this constant in a public module:
Public Const conConnectionQry As String = _
"ODBC;DRIVER={SQL Server};SERVER=YourServerIPAddressGoesHere;" & _
"UID=YourUserIDGoesHere;PWD=DittoPassWord;" & _
"DATABASE=NameOfYourSQLServerDatabase"
Public Sub CreateLocalSQLTable( _
strTable As String, _
strSQL As String, _
Optional bolExportExcel As Boolean _
)
Dim qdf As DAO.QueryDef
Dim strQuery As String
10 On Error GoTo ErrorHandler
20 strQuery = "qryTemp"
30 DoCmd.Close acTable, strTable
40 DoCmd.DeleteObject acQuery, strQuery
50 DoCmd.DeleteObject acTable, strTable
60 Set qdf = CurrentDb.CreateQueryDef(strQuery)
70 With qdf
80 .Connect = conConnectionQry
90 .SQL = strSQL
100 .Close
110 End With
120 strSQL = "Select * INTO " & strTable & " FROM " & strQuery
130 CurrentDb.Execute strSQL
140 DoCmd.DeleteObject acQuery, strQuery
150 If bolExportExcel Then
Dim strFile As String
160 strFile = CurrentProject.Path & "" & strTable & _
Month(Date) & Day(Date) & Year(Date)
170 If Dir(strFile) <> "" Then
180 Kill strFile
190 End If
200 MsgBox "Table " & strTable & " is ready for export to Excel"
202 DoCmd.TransferSpreadsheet acExport, , strTable, strFile, True
204 FollowHyperlink strFile & ".xlsx"
210 End If
ExitProcedure:
220 Set qdf = Nothing
230 Exit Sub
ErrorHandler:
240 Select Case Err.Number
Case 3376, 3010, 7874, 2059, 7873
250 Resume Next
260 Case Else
270 MsgBox Err.Description, vbInformation, "Error"
280 GoTo ExitProcedure
290 End Select
End Sub
The procedure takes three arguments:
- strTable: Name you wish to use for your local Access table
- strSQL: SQL statement used to retrieve data from SQL server. You can therefore extract multi-table joins into a local Access table.
- bolExportExcel: Boolean variable you pass-along if you wish to also extract the data into Excel
Example 1: Extract one table from SQL Server called tblCustomers
CreateLocalSQLTable "tblCustomers_SQL", "Select * from tblCustomers"
Notice how I appended the _SQL to my local Access table name. I will later delete all _SQL tables on program exit.
Example 2: Multi-table join extract
CreateLocalSQLTable "tblCustomers_SQL", "Select CustomerID, CustomerName, CustomerStatus from tblCustomers Inner Join tblCustomerStatus On tblCustomers.CustomerStatusID = tblCustomerStatus.CustomerStatusID", True
In the second example the code will also launch Excel with the extracted data, allowing my customer easy Access to data from SQL Server without the use of ODBC in Excel.
Add indexes and primary keys if needed
Once you’ve downloaded the data you may have a need to add indexes or primary keys to your local copy of the table, click here for a post that will walk you through that using Alter SQL commands in Access.
Another Approach using Linked Tables
If you have the SQL Server table already linked to your Access front-end you can extract the data using this procedure:
Public Sub CopyTableLocally(strTable As String)
Dim strSQL As String
10 On Error GoTo CopyTableLocally_Error
20 Application.Echo True, "Working on table " & strTable
30 DoCmd.DeleteObject acTable, strTable & "_SQL"
40 strSQL = "Select * Into " & strTable & "_SQL From " & strTable
50 CurrentDb.Execute strSQL
ResumeExit:
60 On Error GoTo 0
70 Exit Sub
CopyTableLocally_Error:
80 Select Case Err.Number
Case 3376, 3010, 7874, 2059 'Trying to delete an object that does not exist, continue
90 Resume Next
100 Case Else
110 MsgBox "Error " & Err.Number & " (" & Err.Description & _
") in procedure CopyTableLocally of Module mdlGeneral"
120 Resume ResumeExit
130 End Select
140 Resume ResumeXit
End Sub
Some Caveats
- Don’t download too much data. There’s a good reason your data is on SQL Server. Pulling large amount of data only eats up network traffic and may cause contention issues or even deadlocks. I usually use this technique for small reference tables that I later use in a all local joins. If your data is small and the data rarely changes in the table you’re downloading then this code is for you.
- Beware of file bloating: Ideally you should keep your temp tables in a separate Access file, otherwise your front-end may start getting pretty large and require frequent compacting to slim it back down.
Hi,
Thanks a lot of this example….I used under code for create local table access from other database access but this is a error in “qdf.connect”
please help me !
Public Function create_DB_local_table(strTable As String, ByVal strSQLQuery As String) As Boolean
On Error GoTo lbl_error
Dim strFileName As String
Dim strPassword As String
Dim strConnection As String
strFileName = “\\SRV-KHO\D\MIS MASTER\MIS_BE.accdb”
strConnection = “MS Access;PWD=””;DATABASE=” & strFileName
Dim qdf As DAO.QueryDef
Dim strQuery As String
strQuery = “qryTemp”
On Error Resume Next
DoCmd.Close acTable, strTable
DoCmd.DeleteObject acQuery, strQuery
DoCmd.DeleteObject acTable, strTable
On Error GoTo 0
Set qdf = CurrentDb.CreateQueryDef(strQuery)
qdf.Connect = strConnection
qdf.SQL = strSQLQuery
qdf.Close
strSQLQuery = “Select * INTO ” & strTable & ” FROM ” & strQuery
CurrentDb.Execute strSQLQuery
DoCmd.DeleteObject acQuery, strQuery
create_DB_local_table = True
Exit Function
lbl_error:
create_DB_local_table = False
End Function
Hi Juan
Great info – I devour your writes and articles – however they often [and by design] create more questions than they solve – but that is what learning about.
This article has generated such a problem – creating local tables to circumvent heterogeneous joins. Then you caution about importing too much info and bloating the local Access dbase – sensible
However, what is too much info and when do you link or when do you use SP –
What I find difficult is when to link or not to link – when to import to local – or when to use SP.
However, on top of this is a pure design [or good programming technique] learnt the hard way – ie ODBC linked tables MAKE LIFE EASY – and make use of DAO [which has made a resurgence in 2013] – just easy to use as the linked table just becomes part of Access without thinking about the network aspects. However one major disadvantage is break the link and your queries tumble.
One solution is – import data from SQl to local tables – close link – run query on local tables – never break them – can become bloated – but can run query for solution then empty local tables
Recently I created a program solution frontend Access – backend giant SQL Server 2012 – which [I wasn’t told ] was somewhere out there on the network – ODBC links worked well – all worked well – and makes it easy via DAO to view, edit,update – however – once you go realtime this network linkage created a real lag in the package. New records were however created via ADO – open connection – addnew – close. View, edut/Update was DAO.
And then there is a problem that when you open an ADO OLE connection to an SQL dbase table – and you create a Rst – it isn’t then easy to put that data into a local table without a recursive loop.
Question 1. Your 1st approach uses a PTQ +Make Table Query combo – but is this faster than an ADO connection bypassing ACE – creating a RST then just using a recursive loop to populate a local table
Question 2. Your comments on using linked tables in queries -and how do you not do this except by copying data to a local table.
Question 3. If the Sql dbase is not too large – and you do import all relevant tables – then run queries to view your data – if you need to edit – is that easy to do in this situation
Question 4 – or is it better to create views in SQL – link to those via ADO – and so longas these views aren’t too complex [making them read only] edit/update can occur.
Regards HHG
Juan,
Your code works well for queries selecting tables and SQL views, but obviously I can’t use it to get data from a parameterized SQL UDF or SP. My desire is to populate the temp table in Access, and then use the temp table as the record source for my Access report. Question #1 – Is this the best approach to populate my Access report from SQL data? Question #2 – How would I best populate an Access “temp” table from a SQL stored procedure using Access VBA?
Thank you!
Laura
Luara,
Change line 120 to strSQL = strTable and pass along your entire SQL Statement with the UDF or SP. For example, if you have a SP called usp_Dashboard that takes a date as a parameter, pass the following strTable value:
“Exec usp_Dashboard ’12/31/14′” — Notice the single quotes around the date.
I have not tested a UDF but it should work as well.
This January 7th I’ll be doing a session online on how to create dashboards with SQL Server and Access! You can see more details here:
http://accessusergroups.org/europe/event/access-europe-2015-01-07/
Please join us!
Happy New Year!
Hi,
Thanks a lot of this example… I have been looking for a way to do exactly this.. I had tried using passthrough query, but even that was really slow, but this is excellent. Cut the speed by almost Half.
Best Wishes
Anirudha Kadam
[…] When you need to download data into temp tables in Access […]
Hi Juan
Interesting read – and it just goes to show that as always, there are multiple ways to skin a cat…
I have a question. In this sort of scenario – one where I wanted a temporary table, but one which may have relationships to other tables – I’ve always been likely to do the work in two steps. Firstly a DDL SQL statement to create the table , along with its contraints, indexes and foreign keys. Then secondly, a straightforward DML statement to append the data into that table.
I’m certain that in fact this is simply down to “the way I’ve always done it” – but I suppose I’ve also instinctively felt that creating the indexes etc on an empty table and then appending data would be faster than using an ALTER TABLE command once the data already resides there.
Do you have any thoughts on whether one way or other is likely to yield a performance benefit?
Thanks for a thought-provoking read!
Andrew
Hi Andrew,
Thanks for sharing your technique! I can’t tell you for sure if doing the indexes first and then filling the table with data is faster but your approach does require more discipline.
Sometimes a temp table is the derivative of a multi-join query, so I commend you in taking the time to create the table first, I for one use the INTO statement and then ALTER but each of us has our preferences!
Sincerely,
Juan
Hi Juan good day to you! is it possible to create a 3 tier model or application in pure or native ms access app with a server db backend. It says in 3 tier architecture that the business logic or rules is put into separate server hardware..im puzzled if its possible and really applicable in ms access. Although im very satisfied with 2 tier implementation with access, i just want to explore some of the possibilities (no sharepoint). Hoping for your prompt response and tnx so much. erWIN
Erwin, good question.
N-tiered architecture in general depends on having a middleware… this could be a web server, a DLL or something similar sitting between the client and the database server. As such, you wouldn’t use ODBC, OLEDB or similar technologies when you build a middleware but rather implement the functionality in forms of web services using protocols like SOAP or REST or a properitiary interface which the client must support in order to connect to the middleware. N-tiered architecture is a wonderful when you have a need to support multiple numbers of architectures (think of FaceBook or Twitter – they need/want clients running on not just Windows, but also Mac OS X, iOS, Windows phone, Android… list goes on).
Hopefully, it may become clear why in general Access doesn’t really lend itself as a n-tiered client. We typically are more likely to use it in a corporate environment where there everyone is supposed to use same OSes, is constrained in how they can access data (e.g. may not get to it from home unsecured). In that context, n-tiered architecture would possibly be overkill. That doesn’t mean we have to use 2-tiered architecture exclusively. Juan already cited SharePoint as an example. You want another example not using SharePoint? Write procedures to consume a SOAP web service would effectively make Access a client in a n-tiered architecture. Even better, because it’s going over port 80, it may mean it now can use data from sources even when firewall setting is excessively restrictive and there’s no chance of getting the network guy to change it. Microsoft has an example of calling SOAP web service from VBA:
http://msdn.microsoft.com/en-us/library/aa140260(v=office.10).aspx
I hope that help. If you need assistance building a solution that would enable Access to participate in a n-tiered architecture, feel free to make use of our consulting service and call us at 773.809.5456.
Best of luck!
Hi! good day Juan, I would like to ask about N-Tier Architecture. A SQLserver in a server accessed by a client pc with ms access or vb frontend is a client server model or 2-tier. What if both SQLserver and client frontend was installed in the same server but a terminal server and accessed by the client using remoteApp or remote Desktop? still a 2-tier or 1-tier model? is it possible to implement a 3-Tier model in ms access a.k.a MVC? . I’m also an access die hard but instead of using sqlserver as a backend, I find mySQL and Firebird more compatible with access (that is my own observation- migration and conversion is very straight forward and I dont have to worry even with the boolean Y/N field in access). Thanks and more power to you.
Hi Erwin,
Great questions! Yes, it’s still a two tier model regardless if it’s via RDC, WAN or Local network. I’m not sure what MVC is, perhaps you can elaborate?
In regards to a three tier model, here’s one to consider:
Main database on SQL Server
Access database on client PC
SharePoint Database holding temporary or transient data
Access Web Services running on IPad or other web device.
Hope that helps!
Juan
Thanks again Juan,
below is the definition from MS
The Model-View-Controller (MVC) pattern separates the modeling of the domain, the presentation, and the actions based on user input into three separate classes [Burbeck92]:
Model. The model manages the behavior and data of the application domain, responds to requests for information about its state (usually from the view), and responds to instructions to change state (usually from the controller).
View. The view manages the display of information.
Controller. The controller interprets the mouse and keyboard inputs from the user, informing the model and/or the view to change as appropriate.
Controller. The controller interprets the mouse and keyboard inputs from the user, informing the model and/or the view to change as appropriate.