Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsFormsForms ProgrammingQueriesModules / DAO / VBAReports / PrintingMacrosDatabase DesignSecurityConversionImporting / LinkingSQL Server / ADPMultiuser / NetworkingReplicationSetup / ConfigurationDeveloper ToolkitsActiveX ControlsNew UsersGeneral 1General 2
Access DirectoryToolsTutorialsUser Groups
Related Topics
SQL ServerOther DB ProductsMS OfficeMore Topics ...

MS Access Forum / Multiuser / Networking / March 2005

Tip: Looking for answers? Try searching our database.

"local subforms"?

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
OldTimer - 15 Mar 2005 00:39 GMT
I've inherited a mess of an Access project, and am trying to make the least
change possible to fix the problem.  The problem is that multiple users of
this (unsplit) Access 2002 MDB corrupt each other's data in repeatable ways.

I've determined that several of the data-entry forms have "subforms" which
contain data from shared tables.  Thus, any user entering data in a subform
will write over any other users' data in that same subform.

Seems like a dumb design to me.  (I'm a VB/Access newbie, but have 20+ yrs
programming databases, web, assembly, C++, Java, etc.)  I have to believe
there is some Best Practice to avoid this problem, but darned if I can find
it discussed anywhere, although I've "only" looked in the on-line Access
help, microsoft's site, these forums, and elsewhere on the web.  :-/

My thoughts about possible solutions are:

1)  Back the sub-form with a "temp" table (local to the user, not in the
shared database).  Indeed some of the tables in question list themselves as
"temporary tables" but that doesn't seem to help.  I can't tell if such a
"table type" even exists.

2)  Put these "local subform" tables in the FE and delete them from the BE.  
I haven't been successful with this.  Access doesn't seem to like me deleting
and renaming/copying/pasting tables to accomplish this.

3)  Use a different mechanism in the form to create the "subform" (either
from existing data or as the user enters new rows) -- something other than a
table.  Dunno what that would be, but I know it's NOT a dynaset.

4)  Leave the subform tables global and shared, but hack a unique per-user
column into each of them.  That way they can all R/W to the same "temp" table
for the same form, but they won't be stomping all over each others' rows.  
This would require uniquely identifying each user somehow (a new feature).  
It also begs the question of when to clean up orphaned data in those temp
tables.  And it seems to require the most code changing.

= = = = = = =  Some code snippets:  = = = = = = =

The following appears to be setup for one of these forms, deleting all the
rows in one of these temp tables, and filling it with default information
(events that this client is registered for):

Public Sub subPopulateRegistrationListInfo(frm As Form)
   DoCmd.SetWarnings (False)
   DoCmd.OpenQuery "qRegisteredEvents_del"
   DoCmd.OpenQuery "qRegisteredEvents"
   DoCmd.OpenQuery "qRegisteredEventswithinForm_temp_del"
   If frm.txtFormID = "" Or IsNull(frm.txtFormID) Or IsEmpty(frm.txtFormID)
Then
       'do nothing
   Else
       DoCmd.OpenQuery "qRegisteredEventswithinForm"
   End If    
   DoCmd.SetWarnings (True)
   With frm
       .lstRegisteredEvents.RowSource = "tblRegisteredEventswithinForm_temp"
       .lstRegisteredEvents.Requery        
       .lstAvailableEvents.RowSource = "qAvailableEvents_2"
       .lstAvailableEvents.Requery
   End With
End Sub
_ _ _ _ _ _ _ _ _ _

The following appears to traverse one of these subform temp tables,
comparing the original (built from a query when the form was first displayed)
with the final version (after user adds, deletes and mods) to determine how
to update the "real" shared table:

   Set dbs = CurrentDb
   Set qdf = dbs.CreateQueryDef("")
   With qdf
       .SQL = "SELECT tblRegisteredEventswithinForm_temp.EventID " _
       & " FROM tblRegisteredEventswithinForm_temp " _
       & " WHERE (((tblRegisteredEventswithinForm_temp.EventID) Not In
(SELECT RegisteredEvents.EventID " _
       & " FROM RegisteredEvents " _
       & " WHERE (((RegisteredEvents.RegistrationID)= " & lngRegistrationID
& " ));)));"
       Set rst = .OpenRecordset()
       If rst.RecordCount >= 0 Then
           'delete registration

Unfortunately "tblRegisteredEventswithinForm_temp" is just as global as
"RegisteredEvents" is.
_ _ _ _ _ _ _ _ _ _

This appears to fill the temp table on form load:

Private Sub txtSearchField_LostFocus()
   With Me
       ' added  AND ((Events.Terminate)=False))
       .lstAvailableEvents.RowSource = "Select Events.EventID, " _
           & "Events.[Event Abreviation], Events.EventName " _
           & "FROM Events LEFT JOIN tblRegisteredEvents_temp ON " _
           & "Events.EventID = tblRegisteredEvents_temp.EventID " _
           & "WHERE (((Events.[Event Abreviation]) Like """ &
.txtSearchField & "*"")" _
           & " AND ((tblRegisteredEvents_temp.EventID) Is Null)) AND
((Events.Terminate)=False));"    
       .txtSearchField.SetFocus
   End With
End Sub
_ _ _ _ _ _ _ _ _ _

So, to recap, what I have uses shared MDB tables to back a "subform" in a
form, causing multiple users to overwrite each others' data.

My critical question is:  how can I most easily and safely (i.e. least
amount of rewriting) fix this.

A question out of curiousity is:  how SHOULD this have been done?

Thank muchly.

P.S.  Is there any way to find out which queries refer to a table -- even
something crude like "find"???
John Webb - 15 Mar 2005 16:44 GMT
I know it would be a crude method, but simply placing record locking on the
sub form might solve this problem - meaning that all users will be able to
view the data, but only one can edit a row any one at any time.

Indeed, Access does have it's pitfalls, and one of which is record locking.
You most likely wouldn't have run into this problem in any big way
previously, as you would most likely have been using transactions and so
forth - but it is the bane of many an Access developer.
OldTymer - 15 Mar 2005 21:39 GMT
> I know it would be a crude method, but simply placing record locking on the
> sub form might solve this problem - meaning that all users will be able to
> view the data, but only one can edit a row any one at any time.

More than one person doing data entry at a time is a requirement of this
fix.  Since these "temp" tables are cleared, reloaded, and updated as the
related forms are used, simple locking wouldn't cut it this time.  This was a
good thought exercise to ensure that the simplest solution won't work, though!
George Nicholson - 15 Mar 2005 22:14 GMT
>The problem is that multiple users of  this (unsplit) Access 2002 MDB
>corrupt each other's data in repeatable ways.

A *very* well known formula:
Multiple Users + Unsplit database = Data corruption

Split the database before you do ANYTHING else or you are probably wasting
your time. Traditional Backend/Frontend is BE for Data and FE for
Forms-queries-reports, etc. Each user having their own copy of the FE with
links to the tables in the shared BE.

AFAIK, multiple users sharing FE elements (i.e., Forms) will cause more data
corruption problems than anything else.

HTH,
Signature

George Nicholson

Remove 'Junk' from return address.

> I've inherited a mess of an Access project, and am trying to make the
> least
[quoted text clipped - 126 lines]
> P.S.  Is there any way to find out which queries refer to a table -- even
> something crude like "find"???
OldTymer - 16 Mar 2005 16:33 GMT
> Split the database before you do ANYTHING else or you are probably wasting
> your time. [...]  AFAIK, multiple users sharing FE elements (i.e., Forms) will
> cause more data corruption problems than anything else.

Done.  Thanks for emphasizing this.  BUT...

I can see why splitting allows the FE to be further developed without
locking down the whole application (BE+FE).  But why is it necessary to have
multiple copies of the FE for each client (user)?

In an OO paradigm I see the BE as the "object data" and the FE as the
"object methods".  But obviously this is a flawed way to look at it.  Is
there any easy way to summarize the right way to think about it (which would
help me understand why it was broken in the first place)?  Thankya.
OldTymer - 16 Mar 2005 17:35 GMT
> Split the database before you do ANYTHING else or you are probably wasting
> your time.  [...]  AFAIK, multiple users sharing FE elements (i.e., Forms) will
> cause more data corruption problems than anything else.

I split it successfully.  But got:

  Run-time error 3251
  Operation is not supported for this type of object.

on line 7:

1  Private Sub saveClient(lngClientid As Long)
2     Dim dbs As Database
3     Dim rst As Recordset
4     Set dbs = CurrentDb
5     Set rst = dbs.OpenRecordset("Clients")
6     With rst
7        .Index = "primarykey"
8        .Seek "=", lngClientid

According to the thread "Split database, returning error 3251" started
10/20/2004 in the access.formscoding forum, this type of coding approach is
inconsistent with database splitting.

This type of access to tables is used consistently throughout this Access
application.  Translation:  a helluva lot more work for me just to get to a
point where I can START to fix the original problem.  Sigh.
Larry  Linson - 16 Mar 2005 07:29 GMT
> . . .
> My thoughts about possible solutions are:
[quoted text clipped - 5 lines]
> doesn't seem to help.  I can't tell if such a
> "table type" even exists.

There are temporary tables, but there is no "temporary table" table-type.
That said, temporary tables are rarely a solution in a well-designed Access
database; all-too-often, they are a 'crutch' when the designer can't figure
out the appropriate way to handle a situation but are sometimes a very
useful way to improve performance.

> . . .
> 3)  Use a different mechanism in the
[quoted text clipped - 3 lines]
> Dunno what that would be, but I know it's
> NOT a dynaset.

A Dynaset is simply an updateable query result, which can include data from
one or multiple tables. If you meant a Recordset opened in VBA code, then,
no, that is not an appropriate Record Source for a Form embedded in a
Subform Control.

> 4)  Leave the subform tables global
> and shared, but hack a unique per-user
[quoted text clipped - 4 lines]
> This would require uniquely identifying
> each user somehow (a new feature).

Applying a minimal level of Access security will force users to log in and
you can get the name from the built-in CurrentUser function. Temporary
tables should normally be created in the individual user's front-end
database or on the individual user's machine, and, thus, should not require
a userID field.

> It also begs the question of when to
> clean up orphaned data in those temp
> tables.  And it seems to require the
> most code changing.

If you feel _compelled_ to go with a temporary table solution (and sometimes
it IS the only or most-efficacious way), you'll find an example at MVP Tony
Toews' site of creating a temporary database for the temporary tables. That
avoids problems with database bloat and makes it relatively simple to "clean
up"... as you just delete the temporary database created by the user when
the application closes or when you have "handled" the collected data. If you
don't care about bloat and put the tables in the Front End , then executing
a Delete Query at application Close, or after you do something with the
temporary table data, should be effective in cleaning up.

> A question out of curiousity is:  how
> SHOULD this have been done?

Without additional detail on what you are trying to accomplish, it is
difficult to propose a method. It does sound as if you have done the basics:
front-end for each user and linked tables in the backend.

Unless your requirements are very out-of-the-ordinary, it is unlikely that
multiple users will have a need to concurrently update the same records.
That is why appropriate locking specifications usually are sufficient. If,
in practice, it turns out that there are frequent "collisions", then you can
add error-handling to accomodate them; but the logic of deciding which
update is the "authoritative" one is likely to be the hardest part.

I'd be astonished if you can't accomplish this without resorting to
temporary tables, but if you do, the temporary table will be created in the
individual user's front end database, or, better, the temporary database
containing the temporary table will be created on the user's machine.

> P.S.  Is there any way to find out which
> queries refer to a table -- even
> something crude like "find"???

Two third-party "find and replace" packages that are widely used are Speed
Ferret, http://www.moshannon.com and Find & Replace,
http://www.rickworld.com. The first is commercial software, the second is
shareware. I have used both in times past and found them to be very useful.
(I have no business connection with either, just for the record.)

The best collection of information and links regarding Access in a multiuser
environment that I have found is at MVP Tony Toews' site,
http://www.granite.ab.ca/accsmstr.htm. Some of my "introductory level"
thoughts are in a presentation at
http://appdevissues.tripod.com/downloads.htm.

Best of luck with your project.

 Larry Linson
 Microsoft Access MVP
OldTymer - 17 Mar 2005 15:11 GMT
Here is an update on where I ended up with all this:

I gave up.

I ran out of time for this project - 3.5 days of effort to just get some
kind of fix in.  I'm sure an Access / VB wizard could have rewritten all the
necessary portions of this, and tested and deployed it in that amount of time.

But in my opinion this should not have been so difficult.  I tried many
avenues of solutions, consulted these forums and other resources, etc.

This MDB has approx. 100 tables, 300 queries, 30 forms, 25 reports (many
unused - but which ones??), etc.  Not trivial, but not massive either.

BTW, my most recent attempt was to use the /excl switch to enforce one user
at a time in the database.  It did keep more than one use from starting it
up.  But for some reason it broke a mail-merge capability (the generated Word
doc didn't have any merge data).

In my opinion a "mature" application like this (Access) should not have so
many gotchas.  For example, why was it so easy to originally write it in a
multi-user hostile way, especially when (it sounds like) there is an
alternate way to write the code which is multi-user friendly?

I'm afraid this little project has done nothing to reduce my cynicism of
Things MS.  But I do appreciate the help folks have given in this forum.  
Better luck to you all.
George Nicholson - 17 Mar 2005 20:52 GMT
To be fair, Access is trying to fill the need for a very broad market: the
construction of single-user desktop dbs as well as multi-user.
Access allows casual users and novices to build pretty sophisticated dbs.
If that db is meant for a single-user, great. If the db is meant for a
broader audience then a novice can easily end up building a very
problem-prone app.  The more sophisticated the app you want to build (or
fix), the more there is to learn.

Elements that are very forgiving when only one user is involved aren't when
scaled upward (Example: in one partial code sample you provided, the
question is raised "why are you opening the entire table and seeking for one
record rather than just running a Select query to pull the one record you
need?"). Therefore "ease of use" can become very deceiving, very quickly.

> For example, why was it so easy to originally write it in a
> multi-user hostile way, especially when (it sounds like) there is an
> alternate way to write the code which is multi-user friendly?

You'd have to ask the original developer that question.  There are half a
dozen ways to do anything. "Best" is relative & requires judgement. It
sounds like they were simply in over their head and did the best they knew
how to do in the time provided them to do it (much like you have just done).

I'm not sure how any of this could be seen as the fault of MS, they've never
marketed Access as anything but a tool.  What people do with a tool is up to
them & their abilities.

Sorry this was so frustrating for you. Hope you have better luck next time.
Signature

George Nicholson

Remove 'Junk' from return address.

> Here is an update on where I ended up with all this:
>
[quoted text clipped - 27 lines]
> Things MS.  But I do appreciate the help folks have given in this forum.
> Better luck to you all.
Deborah Reutershan - 29 Mar 2005 04:51 GMT
Deborah Reutershan - 29 Mar 2005 05:00 GMT
To all microsoft.public.access.multiusers.
I do not wish for ANY persons or news groups to e-mail my public access
folder. They will all be deleted without being read after being sent to JUNK
MAIL. Please appreciate my privacy.
Larry  Linson - 30 Mar 2005 00:08 GMT
> To all microsoft.public.access.multiusers.
> I do not wish for ANY persons or news
> groups to e-mail my public access
> folder. They will all be deleted without
> being read after being sent to JUNK
> MAIL. Please appreciate my privacy.

Deborah,

I'm not sure what your "public access folder" means but assuming it means
that the e-mail you used in your post is real: Such an announcement is
useless against the automated harvesters of addresses from newsgroups for
spamming and it will soon age off most news servers so that few will
actually see it.

Using a non-existent e-mail address in your posting is about the only
approach that works to prevent being spammed, or harrassed. Using a "munged"
address may defeat the address harvesters but will still allow individuals
to contact you... something like
yourfirst.ELIMINATE.yourlast@TO.yourURL.EMAILME.com.

As you see, I have opted for the non-existent address approach.

 Regards,

 Larry Linson
 Microsoft Access MVP
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.