tags:

views:

280

answers:

2

I'm not an Access expert, but am an SQL expert. I inherited an Access front-end referencing a SQL 2005 database that worked OK for about 5000 records, but is failing miserably for 800k records...

Behind the scenes in the SQL profiler & activity manager I see some kind of Access query like:

SELECT "MS1"."id" FROM "dbo"."customer" "MS1" ORDER BY "MS1"."id"

The MS prefix doesn't appear in any Access code I can see. I'm suspicious of the built-in Access navigation code:

DoCmd.GoToRecord , , acNext

The GoToRecord has AcRecord constant, which includes things like acFirst, acLast, acNext, acPrevious and acGoTo.

What does it mean in a database context to move to the "next" record? This particular table uses an identity column as the PK, so is it internally grabbing all the IDs and then moving to the one that is the next highest???

If so, how would it work if a table was comprised of three different fields for the PK?

Or am I on the wrong track, and something else in Access is calling that statement? Unfortunately I see a ton of prepared statements in the profiler.

THanks!

+1  A: 

First is literally the first row in the Recordset. In general, Access accesses data via the equivalent of cursors. So, Next and Previous are moving forward and backwards in the Recordset one row at a time just as you can with SQL Server's cursors. Be careful about depending on the sequence of the rows without an ORDER BY statement in the construction of the Recordset. Although Access is an ISAM, you should not rely on the rows coming in any particular order. Depending on the cursor type, Access will not pull the entire table down but will generally ask for one record at a time. That said, I have seen Access pull entire tables for whatever reason.

Thomas
Stupid character limit! This app isn't very complicated -- just five or so tables, but there are about 300k records in one table and about 800k in another. The app basically allows simple editing of the records.If we're using cursors, how does multi-user editing work? It seems like Access is behaving appropriately, dynamically refreshing the primary key (PK) list at every opportunity. How does the cursor type get specified?There is no recordset here. Instead, I think Access is just pointed at the entire table.
Chris M
@Chris M - RE: Multi-user edit, are we talking about a front-end/backend all Access configuration, an Access frontend with linked tables to a real DBMS, or an Access ADP with a real DBMS? In general, how a RS is opened determines how locks are setup. If the RS was opened using adLockOptimistic, Access will lock the record during the save. If set to adLockPessimistic, it will lock the record while it is being edited. A standard form will generally lock the row during editing. In an ADP setup, I think it simply does an update based on the PK but uses a cursor to cycle from one row to the next.
Thomas
@Thomas - Front end Access 2007, back end SQL 2005 via ODBC. The app uses adLockOptimistic and adLockPessimistic almost interchangeably. I doubt the author knew the difference.The worst part is I think the real problem here is the recordset navigation provided by the inherent control. Somehow I need to prevent Access from repeatedly bringing back the primary keys.I think ultimately I'll wind up modifying the app to completely load the data via stored proc and break the connection to the SQL table. I'll handle all the navigation myself. Thanks!
Chris M
A: 

You have to distinguish between automating Access objects and working with recordsets in code.

In a form, this command has meaning:

  DoCmd.GoToRecord , , acNext

It is nonspecific, and it is not predictable what record it will go to unless you know the ordering of the underlying recordset in the form and the starting record. It navigates you through the recordset stored in the form's edit buffer (which is loaded in the OnOpen event of the form). The command would be used, for instance, in the code behind a command button whose purpose is to navigate records loaded into the form that currentlyl has the focus. I would never leave out the optional arguments if I were to use that command (I almost never do). Instead, I'd identify the specific form I wanted it to apply to:

  DoCmd.GoToRecord acForm, "MyForm", acNext

In traversing a DAO recordset, .MoveNext likewise has no predefined meaning except if you know the ordering and starting record. When you are walking a recordset (something you shouldn't do very often, since it's pretty inefficient; but it depends on the task you need to perform) and need to hit each record, you'd certainly call .MoveNext as part of your loop:

  With rs
    .MoveFirst ' technically not required, as it's the default starting point
    Do Until .EOF
      [do something]
      .MoveNext     
    Loop
  End With

Nothing mysterious there. It's most likely going to be used in code with small numbers of records (large recordsets really oughtn't be navigated sequentially).

In answer to your specific question:

What does it mean in a database context to move to the "next" record? This particular table uses an identity column as the PK, so is it internally grabbing all the IDs and then moving to the one that is the next highest???

...as I said, the next record is determined by the ordering of the recordset being traversed and the starting position. In the case of the form, it's the edit buffer that's being traversed, and as the current record bookmark changes in the edit buffer, the form is updated to load the data for that record. The dynaset is bound to the underlying data table, and when the form's edit buffer is saved, the edited data is written back to the server. While it's being edited, locks may or may not be maintained on the record on the server, but Access/Jet/ACE does keep track of the state of the existing record on the server and the record in the edit buffer and will inform you at save time in Access if the record on the server has been changed since it was loaded into the form's edit buffer.

Now, in a comment, you say the form is bound to the whole table. This is a terrible design no matter whether your data is stored in a Jet/ACE back end data file or in a server database like SQL Server. The only reason Access can get away with it is because it and Jet are rather efficient about pulling data from the data source.

I properly-designed client/server Access front end will not load full tables in forms, but instead ask what filtered recordset you want to load, either 1 or more records at a time. This is only marginally more complicated than binding to a whole table.

As to knowing what cursor types are being used, you shouldn't be worrying about it. By default, Access forms use what Access/Jet/ACE calls dynasets. Each form has a RecordsetType property, and it's set to dynaset by default (read the help file on the meaning of the different recordset types). If you want more control of that, you can (but likely shouldn't) create your recordsets in code and assign them to the form's .Recordset property. This is useful in a few circumstances, such as when you'd like to bind a form to a disconnected recordset, but the point of Access is leveraging its capabilities working with bound data. Assigning your own recordsets still gets you bound controls, and the form events, but is more work than is usually necessary.

Basically, change your forms to load only the subset of records the user needs to work with (that may be one record at a time), and then let everything else get done with Access's default behaviors. If something causes a bottleneck, then troubleshoot that and replace the default behavior with something more efficient.

In other words, avoid premature optimization -- let Access be Access.

And don't worry about what Access is doing behind the scenes unless/until Access does something inappropriate.

David-W-Fenton
Chris M
@David - also, the reason I ask is Access takes 8+ seconds to move from one record in the table to the next record. I believe this happens because 200k to 800k of integers (the primary key for the table) get passed back from back-end SQL database to front-end Access each time the user navigates to a new record in the table. This is on a beefy enough server that is able to deal with properly designed databases orders of magnitude larger (but w/ no Access front-end garbage).
Chris M
I'm sort of surprised that Jet would be pulling so much data. I always assumed that Jet would use Rushmore-like "lazy" retrieval with an ODBC data source just as it does with its own data files. I wouldn't know because I've never bound an Access form to an entire SQL Server table. I only do that with small tables with Jet/ACE back ends.
David-W-Fenton
Your surmise on navigation slowness sounds very odd to me (but then, I've never bound an Access form to an entire SQL Server table). Does the form have a sort order that's different from the clustered index on the SQL Server table? If so, you might try clustering on the index the form uses for the sort order, or creating an indexed view with the form's sort order, and then linking to that instead of the base table.
David-W-Fenton