At the risk of starting an ODBC/OLEDB arguement, does anyone have any best practice suggestions for linking an Access Front End to a SQL Server Backend?
I have read the articles about .ADP vs .MDB and have also been through the DNS-less connections information and agree with the thinking.
My main question is around linking the data and performance. In the past I have found forms to be slow when connected directly to the SQL database, I have tested forms based on ODBC linked tables vs an OLEDB connection in the 'OnOpen' event and found the OLEDB approach to be quicker although not great. Therefore I have implemented routines where the data is copied locally when opening a form, modified, then written back to the database when complete but this has its own problems.
Does anyone have suggestions on the best practice approach for this kind of setup? Am I missing something that will improve my forms that are linked directly to the SQL Server?
Any comments or hints appreciated.
You must reduce network traffic to a minimum, especially acknowledgement exchanges. Do this by running SQL exclusively on the SQL server as much as possible, transmitting only complete rowsets back to the client. At all costs avoid joining data on the server to data in the Access DB. That should get you started.
The basic approach is to simply use linked tables.
You should not be seeing any real difference here when using ODBC. ODBC is the preferred approach and Microsoft has announced the end of oleDB support (and the .net community left oleDB and ADO about 10 years ago).
You can bind a form to these linked tables and achieve good performance. The simply trick is to reduce and limit records pulled. This limiting can be accomplished by using a SIMPLE where clause when you launch those bound forms.
So, if you launch a bound invoice form and supply the where clause on the standard open form command then ONLY the one record is pulled. And if course if you have a bound sub form then only the correct records will be pulled from the server also. So no advantage here by using oleDB or ADO will exist in 99% of your cases.
So as a general development approach you can in most cases you can use bound forms.
Where caution is required is that for some joins and anything with aggregate queries, then you want to use a linked table to a sql view. You can also consider using pass-through for reports, but pass-through tends to be more work, and a linked view means you can keep your existing VBA code that likely (hopefully) used a "where" clause to open those reports and again restrict records pulled down the network pipe.
And for dynamic pass-through to execute server side commands, I use this code:
Dim qdfPass As DAO.QueryDef
Set qdfPass = CurrentDb.QueryDefs("MyPass")
qdfPass.SQL = "exec sp_myProc " & "p1"
qdfPass.Execute
If you need the above to return records, then just go:
Dim qdfPass As DAO.QueryDef
Dim rstData As DAO.recordSet
Set qdfPass = CurrentDb.QueryDefs("MyPass")
qdfPass.SQL = "exec sp_myProc " & "p1"
set rstData = qdfPass.OpenRecordSet
So you can on the fly change/modify the above sql for a pass-through query with very little code. At the end of the day, you thus achieve first rate performance, and do so with very few coding changes to the existing application.
So the current recommend approach here is to use DAO. And do keep in mind that end of life support for oleDB (and ADO) for sql server has now been announced. See here:
http://blogs.msdn.com/b/sqlnativeclient/archive/2011/08/29/microsoft-is-aligning-with-odbc-for-native-relational-data-access.aspx
Related
I am working on a project that will use MS Access as front end and SQL Server for data storage. I don't want to link the tables directly because I don't want to take the speed hit to the front end from the 'live' updates. I can't reference the Access DB through JET etc because it is not based in a fixed location so I can't pass through and run directly from server end
I've got as far as this (vba in access):
Sub inserttoaccess()
strSQL = "insert into AccessTable SELECT * FROM [myserverconnectionstring;].servertable"
CurrentDb.Execute (strSQL), dbFailOnError
End Sub
Sub inserttoserver()
strSQL = "insert into [myserverconnectionstring;].servertable SELECT * FROM AccessTable"
CurrentDb.Execute (strSQL), dbFailOnError
End Sub
Which works, and hopefully demonstrates what I need to do, but I'm looking for a way to do it which doesn't require me to keep referring back to the connection in every query. Maybe a way to link to the server without linking specific tables or a function that I could hardcode the connection string but pass the query, but not sure how that would work...
This is the first time I've been looking at using MS Access and SQL Server together so looking for some input as to how those more experienced approach this please
This Post, answered by HansUp (Sorry not enough rep for an upvote)
specifically this:
INSERT INTO MyNewTable (fld1, fld2)
SELECT first_field, second_field FROM YourPassThruQuery;
provides a possible solution without linked tables
Rather than the approach I was trying to take of inserting to an access table from a sqlserver table I can insert to an access table from an access pass through query
By having the connection string in a couple of pass through queries that relate to all the data I need from the server I can then reference these in the multitude of sub queries relating to user input without having the connection string in each one
The question of whether this is method that should be used for this problem stands, but this seems like a viable solution, or at least compromise to limit the number of connection references
Edit: By combining the above with this and hardcoding the connection string I should be able to only reference the connection once, provided the SQL server tables are all on the same database
Sadly (for some) ADPs are out. ADO is out too.
The current recommendation for Access/SQL Server desktop applications is linked tables and queries over DAO using the SQL Server Native Client, version 11.0 or later. Starting with Access 2013, Microsoft has done a lot of work to make this arrangement perform well with SQL Server and Azure.
As for your question in the comments, whether you need to synchronize local Access tables with remote SQL ones depends on what the application is supposed to do:
If the user expects to be able to work while disconnected from the
network/internet/cloud, then you need to synchronize local tables
But, if you are doing a standard application, where persistent links
to the SQL data are assumed, then just do all of your reads and
writes against linked SQL tables. Use pass-through queries to call stored procedures and functions.
I am converting an Access 2010 database from using DAO in the native database to using SQL Server 2012 accessed by ADO. Everything has gone flawlessly without too many code changes... except for one thing:
I have some very simple temp tables in which I need to delete all the rows before using. There are no joins or constraints on the table -- just three columns, all of which are part of the primary key.
This is the old DAO code which worked in the MDB:
DoCmd.SetWarnings False
DoCmd.RunSQL ("DELETE * FROM Print_Tickets_TEMP;")
DoCmd.SetWarnings True
This is the new ADO code which produces an error in SQL Server:
CurrentProject.Connection.Execute "DELETE FROM Print_Tickets_TEMP;"
Running this ADO code produces an error:
Could not delete from specified tables (err 80004005).
It's obviously pretty basic stuff which works in SSMS and in an Access pass-through query. So, I thinking there must be some sort of permission, locking or property that I'm not setting. Note that Connection.Execute gets used all over the app with INSERTS and UPDATES.
The only info I found when searching for answers related to issues people had deleting from joined tables. As I noted, this is a simple standalone table. Anyway, I'm at my wits end... if anyone has any ideas on what's missing or can explain why I can't/shouldn't be doing this, that would be greatly helpful!
edit: If it helps, this code is in a VBA module being called from an Access form.
If there is a specific stored procedure causing this problem, try adding these lines to the beginning of the proc.
SET NO_BROWSETABLE OFF
SET NOCOUNT ON
If you are querying system tables, use a forward-only cursor (to be safest, avoid ADODB.Recordset altogether).
If you are using SQL Server 7.0: if you have ANSI_WARNINGS set to OFF, see KB #259775; if you are using the ROUND() function, make sure that the parameters you are sending are not null (see KB #199105)
If you are using SQL Server 2000, and are using SELECT DISTINCT on a table with a LEFT JOIN on a view, upgrade to SQL Server 2000 Service Pack 2 (see KB #308547).
If you are using MTS transactions, try to change your approach by using transactions within SQL Server.
If none of the above solves your issue, see KB #243899.
http://tutorials.aspfaq.com/8000xxxxx-errors/80004005-errors.html
I have an MS Access application with data in a separate access mdb. We need to move the data to MS SQL. Previously when I did this is worked very well - dramatic speed improvements. In this new upgrade however, we are seeing some problems with translating of the SQL from Access to SQL.
We have the solution separated into an APP mdb and a DATA MDB. We use Linked-tables to join the two. After migrating the data to SQL server, we created an ODBC link, then re-pointed the linked-tables to the SQL data source over ODBC. We would expect the SQL command generated by Access, to be simply transferred to SQL server, for SQL server to operate on same using its normal efficiency and power.
When I put a trace on SQL Profiler, instead of seeing for example a simple "Delete * from table", we see a series of delete statements, one for each record in the table ...
We see the same thing for updates - where we would expect for example "insert into table1 (select a,b,c from table2)...) to be sent as an SQL string, to be executed on the server, we instead see a series of insert statements, one per row being inserted, being sent to the server.
It seems that Access is trying to work out the logic for everything but select statements client side and not letting MS SQL server take care of things as one would expect.
Anyone experienced this behaviour before and can offer a suggestion to resolve?
thanks.
We have a Visual C++ 6 app that stores data in an Access database using DAO. The database classes have been made using the ClassWizard, basing them on CDaoRecordset.
We need to move from Access to SQL Server because some clients have huge (1.5Gb+) databases that are really slow to run reports on (using Crystal Reports and a different app).
We're not too worried about performance on this VC++ app - it is downloading data from data recorders and putting it in the database.
I used the "Microsoft SQL Server Migration Assistant 2008 for Access" to migrate my database from Access into SQL Server - it then linked the tables in the original Access database. If I open the Access database then I can browse the data in the SQL Server database.
I've then tried to use that database with my app and keep running into problems.
I've changed all my recordsets to be dbOpenDynaset instead of dbOpenTable. I also changed the myrecordsetptr->open() calls to be myrecordsetptr->open(dbOpenDynaset, NULL, dbSeeChanges) so that I don't get an exception.
But... I'm now stuck getting an exception 3251 - 'Operation is not supported for this type of object' for one of my tables when I try to set the current index using myrecordsetptr->->SetCurrentIndex(_T("PrimaryKey"));
Are there any tricks to getting the linked tables to work without rewriting all the database access code?
[UPDATE 17/7/09 - thanks for the tips - I'll change all the Seek() references to FindFirst() / FindNext() and update this based on how I go]
Yes, but I don't think you can set/change the index of a linked table in the recordset, so you'll have to change the code accordingly.
For instance: If your code is expecting to set an index & call seek, you'll basically have to rewrite it use the Find method instead.
Why are you using SetCurrentIndex when you have moved your table from Access to SQL Server?
I mean - you are using Access only for linked table.
Also, as per this page - it says that SetCurrentIndex can be used for table type recordset.
In what context are you using the command SetCurrentIndex? If it's a subroutine that uses SEEK you can't use it with linked tables.
Also, it's Jet-only and isn't going to be of any value with a different back end.
I advise against the use of SEEK (even in Access with Jet tables) except for the most unusual situations where you need to jump around a single table thousands of times in a loop. In all other DAO circumstances, you should either be retrieving a limited number of records by using a restrictive WHERE clause (if you're using SEEK to get to a single record), or you should be using .FindFirst/FindNext. Yes, the latter two are proportionally much slower than SEEK, but they are much more portable, and also the absolute performance difference is only going to be relevant if you're doing thousands of them.
Also, if your SEEK is on an ordered field, you can optimize your navigation by checking whether the sought value is greater or lesser than the value of the current record, and choosing .FindPrevious or .FindNext, accordingly (because the DAO recordset Find operations work sequentially through the index).
I'm using DTS to import data from an Access database to SQL Server 2005. It seems that DTS imports Access queries as tables instead of views, which won't work for me. Is there any way around that?
You can choose to not include the saved queries. (at least you can when using the SSMA - I suggest you use this in place of DTS anyway…it tends to do a better job).
you can find it here:
http://www.microsoft.com/sqlserver/2005/en/us/migration.aspx
It is not clear if you going to continue to use ms-access as the front end here or not?. If you plan to continue using access then you really don’t need to convert those saved queries (views) up to sql server anyway. Most of the saved queries in access will work as before (now with linked tables to sql server).
You only need change/fix those saved quires that run slow. In other words most queries can continue to be used and run as is. It is only the slow ones and especially the ones with aggregate functions (sums, totals etc that process many records, but produce few rows). This types of queries really benefit from being moved up to sql server as a view (you then link to that view from ms-access).
If your not keeping any part of ms-access, then I am afraid there no automated tool for those queries. In these cases I just do a cut + paste from ms-access right into the management studio view builder. Most queries require very little modifications.