trying to minimize Write Conflicts in a multi-user database

P

Paul

I have a multi-user Access 2003 database and lately a number of our users
are running into the Write Conflict message gives them the choice to either
Save Record, Copy to Clipboard or Drop Changes.

The problem occurs in a tabbed form with subforms on the different pages.

I have been told by several developers that one way to minimize the
occurrence of the Write Conflict is to put the main form's controls into a
subform and remove the Record Source from the main form. You then set Child
and Master Field links in the subforms to the value returned by the record
selection combo box on the main form (stored in a text box on the main
form).

In effect, you'd only have one record open at a time from the parent table
instead of loading multiple records into the the main form at once.

Would this in fact help reduce the number of Write Conflicts? I ask because
there are a number of events in the main form the various subforms what
would have to be modified, and I'd like to confirm that it will accomplish
something before I spend the time making those changes.

Thanks in advance,

Paul
 
J

John Spencer

I have a similar set up, but I only have one record open on the parent form at
any time. The user selects from a combobox (or by a search that presents them
with a limited number of records). I use this information to identify which
main record they want. Then I execute a query that returns JUST that one
record to the main form.

It is very fast and works nicely and almost no conflicts ever occur.

John Spencer
Access MVP 2002-2005, 2007-2010
The Hilltop Institute
University of Maryland Baltimore County
 
P

Paul

John,

After reading your message again, it occurred to me I would simplify things
a lot if I kept the main form data there in the main form like you said you
were doing it instead of moving it into a subform like I had planned. If I
did that, what's the best way to load a single record into the main form
from the After Update event of a combo box?

Would it be:

DoCmd.OpenForm stDocName, , , stLinkCriteria

or is there a better way to do it?

Thanks again in advance,

Paul
 
P

Paul

I'm working on implementing John Spencer's description of a parent form that
has only one record open at a time, and three questions occur to me:

1. Do you use DoCmd.OpenForm stDocName, , , stLinkCriteria in the
AfterUpdate event of a combo box to move between records?

2. You would be using a SQL SELECT statement to populate the combo box used
to navigate between records once the form is open, but how do you determine
which record to open when you initially load the form? That is, how do you
extract the value of the first record (or for that matter, any record) in
that SQL statement to use as the stLinkCriteria when you first open the
form?

3. Since the main form has only one record loaded at any time, the normal
navigation buttons won't be able to do anything. Are there any Web sites
that address building custom navigation buttons for single-record forms?

Thanks in advance,

Paul
 
J

Jack Leach

I'm not sure about question 3 (I haven't done this before, but I will
certainly keep it in mind, it seems an excellent idea).

For question 1), you wouldn't be using the boilerplate DoCmd.OpenForm method
and criteria... you presumably already have the form open, you just need to
give it a recordsource...

Private Sub Me.cboRecords AfterUpdate()
Me.Recordsource = "SELECT * FROM table WHERE [ID] = " & Me.cboRecords
Me.Requery
End Sub

Ideally, this would be a saved query name that pulls the value direct from
the combo on the form... performance would be better like that, I believe.

As for Q2, it depends what you want your first record to be... but what I
would do is move the above code into its own procedure rather than the
afterupdate, then you can call the procedure from both the Open event and the
combo AfterUpdate event:


Private Sub psGoToRec(lRecID As Long)
Me.Rowsource = "SELECT * FROM Table WHERE [ID] = " & lRecID
Me.Requery
End If

Private Sub cboRecord_AfterUpdate()
psGoToRec(lRecID)
End Sub

Private Sub Form_Open(Cancel As Integer)
psGoToRec(<your default opening id here>)
End Sub



I would be curious to see a reply for the navigation (and new records)
portion of the question... no lights are shining in my head on that at the
moment...
--
Jack Leach
www.tristatemachine.com

"I haven''t failed, I''ve found ten thousand ways that don''t work."
-Thomas Edison (1847-1931)
 
P

Paul

Thanks for the help with this, Jack. The code you suggested:

Me.Recordsource = "SELECT * FROM table WHERE [ID] = " & Me.cboRecords

works great in the combo box's AfterUpdate event because it retrieves the
desired record instantly, and leaves only the single record in the form,
which is exactly what I want.

However, I'm having trouble getting it to work in the form's Load event
because the combo box is null, and the assignment statement above results in
an error since Me.cboRecords is null. Similarly, if I try to put the value
of the combo box in the criteria field of the form's query, the form is
blank because the value in the combo box is null. Is there any way I can
force the combo box to retrieve one of its own records (the first one would
be ok) as soon as the form loads, so it can provide a non-null value for the
ID in the assignment statement and the criteria of the form's query?

I've tried to Requery the combo box before setting the RecordSource of the
form, but that didn't seem make a difference.

Any suggestions how I can overcome problem of the empty combo box when the
form loads?

Paul
 
T

Tom Wickerath

Hi Paul,

Try setting the Recordsource for the form to this SQL statement (or to a
saved query with this SQL statement):

SELECT * FROM table WHERE 1=0

This query is guaranteed to return zero records. Remove the code that you
had in the Form_Load event, which was apparently running the query that
grabbed criteria from the combo box.

To address your original question about minimizing Write Conflict errors,
Are there any tables that include memo, hyperlink, OLE Object, or the new
multi-value field (Access 2007 .accdb only)? The reason I ask is that these
fields can cause page locking to be invoked, when you might otherwise assume
that record locking is being used. For JET databases, I have gotten into the
habit of breaking memo fields out to a separate table, with a 1:1
relationship, instead of including the memo field in the same table with
other fields. This way, when a user clicks into the memo field on a form,
they have immediately committed any changes to the parent record and
vice-versa, clicking out of the memo field on the form to any other field
commits changes to the memo data. I base this on the following quote from
Microsoft:

"Also, record-level locking is not enabled for Memo data types."

Source: http://support.microsoft.com/kb/275561, under the title: "Record-level
locking".


Another reason for moving memo fields to their own table is so that I never
run
into this situation:
http://support.microsoft.com/kb/296389

Keep in mind that hyperlink and OLE Object fields involve the same pointer
mechanism that memo fields do, so the above discussion applies to these data
types equally well.

The other thing that I've been doing in all my released applications for the
past couple of years is running code at startup, via an Autoexec macro, that
uses ADO to establish record level locking for the first person to open the BE
database. Subsequent users will connect to the BE database with the same
locking
that the initial user establishes:

http://support.microsoft.com/?id=306435

Michael Kaplan points out on his blog site that when a user selects the option
to use Record Level locking, that this is only a request, not a demand. So, by
using the ADO code in the above article, you are essentially demanding record
level locking.

This KB article clearly states this for the new MVF data type in Access 2007
causes page locking:

http://support.microsoft.com/kb/918578


Hope this helps some.

Tom Wickerath
Microsoft Access MVP
http://www.accessmvp.com/TWickerath/
__________________________________________

Paul said:
Thanks for the help with this, Jack. The code you suggested:

Me.Recordsource = "SELECT * FROM table WHERE [ID] = " & Me.cboRecords

works great in the combo box's AfterUpdate event because it retrieves the
desired record instantly, and leaves only the single record in the form,
which is exactly what I want.

However, I'm having trouble getting it to work in the form's Load event
because the combo box is null, and the assignment statement above results in
an error since Me.cboRecords is null. Similarly, if I try to put the value
of the combo box in the criteria field of the form's query, the form is
blank because the value in the combo box is null. Is there any way I can
force the combo box to retrieve one of its own records (the first one would
be ok) as soon as the form loads, so it can provide a non-null value for the
ID in the assignment statement and the criteria of the form's query?

I've tried to Requery the combo box before setting the RecordSource of the
form, but that didn't seem make a difference.

Any suggestions how I can overcome problem of the empty combo box when the
form loads?

Paul
 
J

John Spencer

As for question 3

3. Since the main form has only one record loaded at any time, the normal
navigation buttons won't be able to do anything. Are there any Web sites
that address building custom navigation buttons for single-record forms?

You can build code to move next or move previous based on the combobox you are
using to get the record. You just need to know which record you are on and
where that is in the combobox. Then get the value of the previous/next row in
the combobox, set the combobox to that row and execute the code to select the
record based on that value.

If you need help building that code, post back. If I have some free time
later today, I will try to help.

John Spencer
Access MVP 2002-2005, 2007-2010
The Hilltop Institute
University of Maryland Baltimore County
 
J

Jack Leach

For a combo...

The .ListIndex property (0 based) tells what item you have selected (or -1
for no selection). Use this and the .Column(.ListIndex) to get the the
currently selected record.

The .ListCount (1 based) tells how many list items there are in the source
of the combo. Use this to determine if the current selection is the last
one. If .ListIndex = .ListCount - 1 Then LastRecord = True

You should be able to "select" a record using the Column property:

Me.Combo = Me.Combo.Column(Me.Combo.ListIndex + 1)


This should give you an idea where to start.

I haven't tried yet, but am still a bit curious about how to handle a
recordsource of the main form for a new record... I *think* that, per Tom's
suggestion, you can set the SQL to WHERE 1 = 0 (which will display no
records), and be able to enter a new record this way. I think that's the
case, but haven't tested yet.

This really is a great idea, I will also begin to do some redesigning
(haven't had an issue with write conflicts as of yet, but that's not to say I
won't...) And many thanks to Tom for making the points on Memo, OLE and MVF
fields... I was not aware of this either.

--
Jack Leach
www.tristatemachine.com

"I haven''t failed, I''ve found ten thousand ways that don''t work."
-Thomas Edison (1847-1931)
 
P

Paul

Tom,

It was your excellent article at

http://www.accessmvp.com/TWickerath/articles/multiuser.htm

that launched me on this path in the first place. I've got two tables with
a memo field, and I plan to isolate them into separate 1 to 1 tables in the
coming weeks. I already took a run at it and quickly realized that I'll be
spending several weeks debugging all the changes required by the new table
structure. I thought that in the meantime, I'd take the step of loading one
record at a time into the main form, instead of multiple records.

I found your suggestion to set the RecordSource in the form's Load event to
SELECT * FROM table WHERE 1=0

to be very helpful, because now when the form loads all of the controls and
subforms are visible.

However, they, along with my combo box, are also empty. Is there a way I
could get both the combo box and the form to display the first record in the
recordset when the form first opens?

(If it requires DAO or ADO code, a dumbded-down answer in the form of the
actual code would be most welcome).

Thanks

Paul
 
P

Paul

If you need help building that code, post back.

John,

YES, PLEASE! I'd never be able to figure it out on my own.

I've taken several runs at trying to understand DAO and ADO coding, but I
still struggle with it. The only MS Access book I ever found that explained
it in a way I could understand it was the manual for Access 2.0, which I
only borrowed temporarily. But I didn't use it right away, and didn't
manage to retain what I understood from it.

If you know of any Web sites that explain the basics of DAO and ADO, I'd
love to check them out.

And thanks for your help with this.

Paul
 
P

Paul

Jack,

I tried using

Me.Combo = Me.Combo.Column(Me.Combo.ListIndex + 1)

and the combo box and form still open with no records.

I also tried assigning a number to Me.Combo.ListIndex,

Me.Combo.ListIndex = 0

but I got and error message saying I've "Used the ListIndex property
incorrectly.

As I've said in other replies in this thread, I've got the form opening with
no records initially, and as soon as I make the selection in the combo box,
the selected record appears in both the combo box and the form. What I'm
trying to accomplish is for both the combo box and the form to be populated
with the "first" record in the recordset (ListIndex = 0) when the form
loads. Please let me know if you have any ideas about how to accomplish
this.

I've asked the same question in my reply to Tom's last message, and I'm not
sure if it's proper newsgroup ettiquette to repeat the same question to
another participant in the conversation, but if not, I apologize for the
redundancy.

Paul
 
G

GBA

just a sanity check as to whether this string is purely theoretical or not.
Have done many many multi user applications with nary a write
conflict....Access out of the box works really well in this area and so I
wonder if the issue is theoretical or real....
 
P

Paul

No, this is a real issue for me. I've spent the last year working on a very
real project management database in Access 2003. At the moment, I have 40
users, and a week from Monday, I'm going to have about 100. Just within the
past three weeks, my users have started to encounter the Write Conflict
error I described in my first post.

I work in a very real state government agency. My colleagues are using it
to manage their projects, and I'll list (hey - you asked if it was real)
just a few of the features that make it more than a card filing system:

* Every night my VBA code runs 42 queries that append and update data in our
application from an Oracle database, SQL Server and another Access database.
They also upload different data to that other Access database.
* In addition to projects, it also manages leases, contacts, activity nd
documents.
* It uses the OS login name to distinguish between editable and read only
records, depending on whether the user is a team member of the project
* there are 5 user classes - user, admin, admin User, read only and a 5th
one that I can't recall at the moment - and depending on which class the
user belongs, different forms and different form controls will be presented
to the user.
* It's also a document processing file manager. My users process lots of
contracts and documents, and my application enables them to select from
hundreds of documents in Word, Excel, PDF and html from a shortcut menu
sysem, and it populates fields in the documents with data in the database.
It also saves the files into the project folder on the network drive, so the
user doesn't have to navigate through Windows explorer to find the project
folder.

We have conservatively estimated that the file management module I just
described is saving our agency and the taxpayer the time equivalent of over
$500,000 per year.

Maybe you don't have a problem because you designed your database better
than I did. I do have memo fields in two tables, and Tom Wickerath has
pointed out that could be the problem.

But yes, it's a real database, and I'm dealing with a very real problem.

Paul
 
B

Banana

Paul said:
No, this is a real issue for me. I've spent the last year working on a very
real project management database in Access 2003. At the moment, I have 40
users, and a week from Monday, I'm going to have about 100. Just within the
past three weeks, my users have started to encounter the Write Conflict
error I described in my first post.

Other posters has given you many excellent advices. I just want to rule
out one more cause of write conflict: It is possible that the write
conflict errors are bogus because of VBA coding stepping on itself or on
the Access. A good way to do this is to have VBA execute a separate
query that modifies the same record that is being edited in the form.
This comes out as two separate connection and of course the software
(whether it's Access or the backend RDBMS) has no idea that the two
separate connection are actually the same application/user and perceive
it as deadlock for this reason.

Therefore, if your VBA code behind the forms does use queries that
update the same record or maybe different record on the same table that
could be on the same page, this will cause write conflicts.

If your code doesn't have that, then you're probably good and want to
look at others' excellent solutions.

Best of luck.
 
P

Paul

Interesting point, Banana. I am running several queries in the AfterUpdate
event of several controls that append or upate other records in the same
table. I wonder if the problem occurs when those records are on the same
page. Howevr, there are no memo fields in those tables, but then if I
understand it right, in those cases my option settings should enable Acces
to only lock the edited record, not a group (page) of records.
 
B

Banana

Paul said:
Interesting point, Banana. I am running several queries in the AfterUpdate
event of several controls that append or upate other records in the same
table. I wonder if the problem occurs when those records are on the same
page. Howevr, there are no memo fields in those tables, but then if I
understand it right, in those cases my option settings should enable Acces
to only lock the edited record, not a group (page) of records.

Well, depends on the context. For example, if you are using a continuous
view or datasheet view, then you are certainly locking a group of
records. As for the locking behavior, few points.

1) According to this:

http://office.microsoft.com/en-us/access/HP052408601033.aspx

"If you use record-level locking, Access locks only the record you are
editing. This becomes the default behavior for access to data through a
form, a datasheet, and code that uses a recordset object to loop through
records, but not through action queries, nor through code that performs
bulk operations using SQL statements."

IOW, record-level locking is only available on forms and not via
queries, so queries could then conflict with the forms' locking.

2) I also want to say that the setting you specify is a merely request
and not a request so there is always a chance that the locking could get
escalated by Access. However I was unable to find the supporting article
for this, so take the second assertion with a huge grain of salt.

Either way, I can tell you that I've been personally bitten by running
action queries against the same table in middle of editing. But by being
aware of how this works, we can certainly work with it. One posssible
example is to avoid running queries while a record is dirty. Assuming no
locks (or rather, more formally known as optimistic locking), running
queries in say, form's AfterUpdate is usually fine (but a control's
AfterUpdate may be insufficient because the record may be still dirty).

Be aware, though, that this is not a complete solution in fact that you
are just changing around the timeframe of when updates occurs and
actually may be increasing the risk of contention in a high-concurrency
scenario.

Alternatively, working through the form's model is probably the best way
to eliminate locking problems because that means you are now re-using
the same 'connection' to do updates on other stuff. You have the
Recordset property, RecordsetClone property, Bookmark property and
several methods for DAO.Recordset objects to accomplish all this, and
indeed, I do this for where I want to edit a related record on the same
table. For bulk operations, I prefer action queries anyway, so that's
back to the first suggestion.

HTH.
 
P

Paul

Based on what you've said, and what I read in that Microsoft article you
referenced, it would probably be a good idea to replace some of my action
queries was DAO code, because I am performing action queries on continuous
(tabular) view subforms.

One approach I've already tried is to have the Timer event of the form save
the form's record every few minutes to prevent against people leaving an
edited record unsaved for long periods of time.
Thanks for the explanations, Banana.

P
 
T

Tom Wickerath

Hi Paul,

Thanks for the nice compliment on my Multiuser Applications paper!
However, they, along with my combo box, are also empty. Is there a way I
could get both the combo box and the form to display the first record in the
recordset when the form first opens?

Sure, you just need to uniquely identify the record in question, and use the
appropriate WHERE clause. So, instead of using WHERE 1=0, use something like:

WHERE [PrimaryKeyFieldName] = NumericValue (for a numeric PK field)
or
WHERE [PrimaryKeyFieldName] = 'TextValue' (for a text-based PK field)

Notes:
Use the quotes surrounding the TextValue for a text-based value.
You can specify a different field as well, as long as the field has a unique
index, in order to retrieve just one record.


Tom Wickerath
Microsoft Access MVP
http://www.accessmvp.com/TWickerath/
__________________________________________
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top