Petr Danes
2008-07-03 21:30:44 UTC
I hope someone can give me a bit of general advice on some fundamental
approaches here.
I have a normal Access database of paleontological records that started out
as a single-user app, grew to a multiple-user, but one-at-a-time app that
lives on a network machine (the users just telephoned each other when there
were use conflicts) and now threatens to become a full-blown network app,
with around a dozen users (not all simultaneous, probably no more than two
or three at a time). The first step, from single use to shared one-at-a-time
use did not require much, the network is insanely under-utilized and
in-house traffic just isn't an issue. Opening the Access database over the
network in its original form was marginally slower than local use, but once
open, no speed difference was noticeable.
This next step is clearly going to be considerably more work. The DB engine
will most likely be MySQL, largely for financial reasons (although I'm
considering SQL Server as well - thoughts on that are also welcome). I have
downloaded and installed it on my development machine, split the app and am
already having speed problems with my more complicated queries.
The main user interface is a linked form-subform. The main table has around
60,000 records, the subtable around 50,000, and about twenty associated
tables feeding comboboxes, some with a couple dozen records, most with
several hundred and a few into the thousands. Some main records have over a
hundred subrecords, others, obviously, have none. The subform shows an
abbreviated view of the records associated with the currently displayed main
record, with some fields from the associated tables concatenated into
single, non-editable text fields. The query for the subform is fairly
complicated, joining most of the associated tables in the database, for the
concatenated fields. (It's included at the end of this post, just for show.
Built with the QBE, but works fine.)
There is a button on the subform that opens a more detailed form in dialog
mode, first saving any current changes on the abbreviated subform, which
allows the user to modify subrecords. It jumps to the record/subrecord
currently displayed on the main form/subform, allows the user to move
forward and backward through the entire database, and when closed, changes
the display on the main form/subform to the record/subrecord it was on when
closed.
All this works fine for a single person using the Access mdb. I don't even
bother compiling it, occasionally a user needs to build a custom query for
something I hadn't anticipated, so I've left it open for them. But for a
real network approach, I'll have to consider the traffic in and out of the
DB engine. So here are a few questions:
1. There are posts in the NG talking about not having a recordsource on a
form until the user does something. Would that be appropriate here? If so,
how is it done? Would I have an unbound form, and use code to fill in the
recordsource property on the form? What would the recordsource be? A query
returning only one record? How about the subform? Does it stay linked,
returning only subrecords associated with the one main record?
2. My main form currently scrolls through the main table in single-view
mode, with the linked subform scrolling the associated records in continuous
view. Would manually modifying the recordsource preclude using the Access
navigation buttons? If so, how does the user get to a record? Ask them each
time for a record identifier? That seems clumsy. Also, what if the user does
not know the proper ID for a record? The ID for main records is a
combination of year and number within year, but the numbers have a variety
of forms, not necessarily consistent. Scrolling allows the user to find a
record easily, even without knowing the exact ID. (The data is a hash of
record-keeping, collected from many people over many years, much of it
pre-dating any sort of modern technology, as far back as the 1800s. A
subrecord may have an ID of 134, 134/a, a/134, BR-134, BR - 134, BR/134 and
so on. It's a zoo in that regard, but is not subject to being changed,
period.) How would the user locate a record? Read the table and load up a
combobox full of IDs? Is that different from simply having the table as a
recordsource?
3. What actually happens in the client-server dialog? When I open a form
with a table as recordsource, will Access request the ENTIRE table at once?
Is the entire table then locked? Or does it request only the record
currently displayed? What about the subform? Are all the associated
subrecords requested at once? All locked? If not, how does the server know
which of the subrecords is being modified and which are just being examined?
4. I do not anticipate much conflict between users. They each have their own
set of records which they maintain and rarely will they be interested in
anyone else's. The associated tables may have info added by various users,
although not often. When such is added, however, other users should have it
available, to keep from adding it again, or in a slightly different form
(e.g., Korycany layer, Korycany formation). When one user adds a record to
such a table, how do other users find out? Try to add it themselves and get
an error? Allow only a special user to add records? That would be too
restrictive here. Now I do it by simply trapping the NotInList event and
popping up a dialog box to allow an addition. Does that work over a network
connection?
I've certainly got a bit of studying to do, but most of the literature I
find deals with specifics of how to do something, which isn't appropriate
for me yet. First I need to decide what, in general, I need to do, and I
haven't found any high-level, overview material dealing with the subject.
The nuts and bolts come later, at the moment I don't even really have a
plan.
Sorry about all the kindergarten-level questions, but the last time I worked
on a real network DB application was decades ago, on a Dec PDP 11/70 running
RSTS/E and programming in Basic+2. Things have changed a bit since then.
Pete
--
This e-mail address is fake, to keep spammers and their auto-harvesters out
of my hair. If you want to get in touch personally, I am 'pdanes' and I use
yahoo mail. But please use the newsgroup when possible, so that all may
benefit from the exchange of ideas.
Query for subform:
Akces - main table - 60,000 records
AkcesPodrobnosti - subtable - 50,000 records
TableOfxxxxxxx - associated tables for comboboxes - several dozen to several
thousand records each
SELECT Akces.Rok, Akces.Akcesit, AkcesPodrobnosti.*, [system]+" " &
[series]+" " & [stage]+" " & [substage]+" " & [LithographicUnit]+" " &
[LithographicSubUnit]+" " & [zone] AS Stratigrafie, [Group]+" " & [Order]+"
" & [family]+" " & [genus]+" " & [subgenus]+" " & [species]+" " &
[subspecies]+" " & [Authors] AS Taxonomie, TableOfCountrys.Cesky+" - " &
TableOfCountrys.Country AS Kountry, [EvidenceLetter]+" " &
Trim(Str([EvidenceNumber]))+" " & "("+[EvidenceExtra]+")" AS 2Evidence,
[Rok]+"-" & [akcesit] AS Akces, [Building]+" " & [Room]+" " & [Cabinet]+" "
& [Drawer] AS Lokace
FROM Akces INNER JOIN (((((((((((((((AkcesPodrobnosti LEFT JOIN
TableOfSystems ON AkcesPodrobnosti.SystemAutoID =
TableOfSystems.SystemAutoID) LEFT JOIN TableOfSeriess ON
AkcesPodrobnosti.SeriesAutoID = TableOfSeriess.SeriesAutoID) LEFT JOIN
TableOfStages ON AkcesPodrobnosti.StageAutoID = TableOfStages.StageAutoID)
LEFT JOIN TableOfSubStages ON AkcesPodrobnosti.SubStageAutoID =
TableOfSubStages.SubStageAutoID) LEFT JOIN TableOfLithographicUnits ON
AkcesPodrobnosti.LithographicUnitAutoID =
TableOfLithographicUnits.LithographicUnitAutoID) LEFT JOIN
TableOfLithographicSubUnits ON AkcesPodrobnosti.LithographicSubUnitAutoID =
TableOfLithographicSubUnits.LithographicSubUnitAutoID) LEFT JOIN
TableOfZones ON AkcesPodrobnosti.ZoneAutoID = TableOfZones.ZoneAutoID) LEFT
JOIN TableOfGroups ON AkcesPodrobnosti.GroupAutoID =
TableOfGroups.GroupAutoID) LEFT JOIN TableOfOrders ON
AkcesPodrobnosti.OrderAutoID = TableOfOrders.OrderAutoID) LEFT JOIN
TableOfFamilys ON AkcesPodrobnosti.FamilyAutoID =
TableOfFamilys.FamilyAutoID) LEFT JOIN TableOfGenuss ON
AkcesPodrobnosti.GenusAutoID = TableOfGenuss.GenusAutoID) LEFT JOIN
TableOfSubGenuss ON AkcesPodrobnosti.SubGenusAutoID =
TableOfSubGenuss.SubGenusAutoID) LEFT JOIN TableOfSpeciess ON
AkcesPodrobnosti.SpeciesAutoID = TableOfSpeciess.SpeciesAutoID) LEFT JOIN
TableOfSubSpeciess ON AkcesPodrobnosti.SubSpeciesAutoID =
TableOfSubSpeciess.SubSpeciesAutoID) LEFT JOIN TableOfCountrys ON
AkcesPodrobnosti.CountryAutoID = TableOfCountrys.CountryAutoID) ON
Akces.AkcesAutoID = AkcesPodrobnosti.AkcesAutoID
ORDER BY Akces.Rok, Akces.Akcesit, AkcesPodrobnosti.EvidenceLetter,
AkcesPodrobnosti.EvidenceNumber, AkcesPodrobnosti.EvidenceExtra;
approaches here.
I have a normal Access database of paleontological records that started out
as a single-user app, grew to a multiple-user, but one-at-a-time app that
lives on a network machine (the users just telephoned each other when there
were use conflicts) and now threatens to become a full-blown network app,
with around a dozen users (not all simultaneous, probably no more than two
or three at a time). The first step, from single use to shared one-at-a-time
use did not require much, the network is insanely under-utilized and
in-house traffic just isn't an issue. Opening the Access database over the
network in its original form was marginally slower than local use, but once
open, no speed difference was noticeable.
This next step is clearly going to be considerably more work. The DB engine
will most likely be MySQL, largely for financial reasons (although I'm
considering SQL Server as well - thoughts on that are also welcome). I have
downloaded and installed it on my development machine, split the app and am
already having speed problems with my more complicated queries.
The main user interface is a linked form-subform. The main table has around
60,000 records, the subtable around 50,000, and about twenty associated
tables feeding comboboxes, some with a couple dozen records, most with
several hundred and a few into the thousands. Some main records have over a
hundred subrecords, others, obviously, have none. The subform shows an
abbreviated view of the records associated with the currently displayed main
record, with some fields from the associated tables concatenated into
single, non-editable text fields. The query for the subform is fairly
complicated, joining most of the associated tables in the database, for the
concatenated fields. (It's included at the end of this post, just for show.
Built with the QBE, but works fine.)
There is a button on the subform that opens a more detailed form in dialog
mode, first saving any current changes on the abbreviated subform, which
allows the user to modify subrecords. It jumps to the record/subrecord
currently displayed on the main form/subform, allows the user to move
forward and backward through the entire database, and when closed, changes
the display on the main form/subform to the record/subrecord it was on when
closed.
All this works fine for a single person using the Access mdb. I don't even
bother compiling it, occasionally a user needs to build a custom query for
something I hadn't anticipated, so I've left it open for them. But for a
real network approach, I'll have to consider the traffic in and out of the
DB engine. So here are a few questions:
1. There are posts in the NG talking about not having a recordsource on a
form until the user does something. Would that be appropriate here? If so,
how is it done? Would I have an unbound form, and use code to fill in the
recordsource property on the form? What would the recordsource be? A query
returning only one record? How about the subform? Does it stay linked,
returning only subrecords associated with the one main record?
2. My main form currently scrolls through the main table in single-view
mode, with the linked subform scrolling the associated records in continuous
view. Would manually modifying the recordsource preclude using the Access
navigation buttons? If so, how does the user get to a record? Ask them each
time for a record identifier? That seems clumsy. Also, what if the user does
not know the proper ID for a record? The ID for main records is a
combination of year and number within year, but the numbers have a variety
of forms, not necessarily consistent. Scrolling allows the user to find a
record easily, even without knowing the exact ID. (The data is a hash of
record-keeping, collected from many people over many years, much of it
pre-dating any sort of modern technology, as far back as the 1800s. A
subrecord may have an ID of 134, 134/a, a/134, BR-134, BR - 134, BR/134 and
so on. It's a zoo in that regard, but is not subject to being changed,
period.) How would the user locate a record? Read the table and load up a
combobox full of IDs? Is that different from simply having the table as a
recordsource?
3. What actually happens in the client-server dialog? When I open a form
with a table as recordsource, will Access request the ENTIRE table at once?
Is the entire table then locked? Or does it request only the record
currently displayed? What about the subform? Are all the associated
subrecords requested at once? All locked? If not, how does the server know
which of the subrecords is being modified and which are just being examined?
4. I do not anticipate much conflict between users. They each have their own
set of records which they maintain and rarely will they be interested in
anyone else's. The associated tables may have info added by various users,
although not often. When such is added, however, other users should have it
available, to keep from adding it again, or in a slightly different form
(e.g., Korycany layer, Korycany formation). When one user adds a record to
such a table, how do other users find out? Try to add it themselves and get
an error? Allow only a special user to add records? That would be too
restrictive here. Now I do it by simply trapping the NotInList event and
popping up a dialog box to allow an addition. Does that work over a network
connection?
I've certainly got a bit of studying to do, but most of the literature I
find deals with specifics of how to do something, which isn't appropriate
for me yet. First I need to decide what, in general, I need to do, and I
haven't found any high-level, overview material dealing with the subject.
The nuts and bolts come later, at the moment I don't even really have a
plan.
Sorry about all the kindergarten-level questions, but the last time I worked
on a real network DB application was decades ago, on a Dec PDP 11/70 running
RSTS/E and programming in Basic+2. Things have changed a bit since then.
Pete
--
This e-mail address is fake, to keep spammers and their auto-harvesters out
of my hair. If you want to get in touch personally, I am 'pdanes' and I use
yahoo mail. But please use the newsgroup when possible, so that all may
benefit from the exchange of ideas.
Query for subform:
Akces - main table - 60,000 records
AkcesPodrobnosti - subtable - 50,000 records
TableOfxxxxxxx - associated tables for comboboxes - several dozen to several
thousand records each
SELECT Akces.Rok, Akces.Akcesit, AkcesPodrobnosti.*, [system]+" " &
[series]+" " & [stage]+" " & [substage]+" " & [LithographicUnit]+" " &
[LithographicSubUnit]+" " & [zone] AS Stratigrafie, [Group]+" " & [Order]+"
" & [family]+" " & [genus]+" " & [subgenus]+" " & [species]+" " &
[subspecies]+" " & [Authors] AS Taxonomie, TableOfCountrys.Cesky+" - " &
TableOfCountrys.Country AS Kountry, [EvidenceLetter]+" " &
Trim(Str([EvidenceNumber]))+" " & "("+[EvidenceExtra]+")" AS 2Evidence,
[Rok]+"-" & [akcesit] AS Akces, [Building]+" " & [Room]+" " & [Cabinet]+" "
& [Drawer] AS Lokace
FROM Akces INNER JOIN (((((((((((((((AkcesPodrobnosti LEFT JOIN
TableOfSystems ON AkcesPodrobnosti.SystemAutoID =
TableOfSystems.SystemAutoID) LEFT JOIN TableOfSeriess ON
AkcesPodrobnosti.SeriesAutoID = TableOfSeriess.SeriesAutoID) LEFT JOIN
TableOfStages ON AkcesPodrobnosti.StageAutoID = TableOfStages.StageAutoID)
LEFT JOIN TableOfSubStages ON AkcesPodrobnosti.SubStageAutoID =
TableOfSubStages.SubStageAutoID) LEFT JOIN TableOfLithographicUnits ON
AkcesPodrobnosti.LithographicUnitAutoID =
TableOfLithographicUnits.LithographicUnitAutoID) LEFT JOIN
TableOfLithographicSubUnits ON AkcesPodrobnosti.LithographicSubUnitAutoID =
TableOfLithographicSubUnits.LithographicSubUnitAutoID) LEFT JOIN
TableOfZones ON AkcesPodrobnosti.ZoneAutoID = TableOfZones.ZoneAutoID) LEFT
JOIN TableOfGroups ON AkcesPodrobnosti.GroupAutoID =
TableOfGroups.GroupAutoID) LEFT JOIN TableOfOrders ON
AkcesPodrobnosti.OrderAutoID = TableOfOrders.OrderAutoID) LEFT JOIN
TableOfFamilys ON AkcesPodrobnosti.FamilyAutoID =
TableOfFamilys.FamilyAutoID) LEFT JOIN TableOfGenuss ON
AkcesPodrobnosti.GenusAutoID = TableOfGenuss.GenusAutoID) LEFT JOIN
TableOfSubGenuss ON AkcesPodrobnosti.SubGenusAutoID =
TableOfSubGenuss.SubGenusAutoID) LEFT JOIN TableOfSpeciess ON
AkcesPodrobnosti.SpeciesAutoID = TableOfSpeciess.SpeciesAutoID) LEFT JOIN
TableOfSubSpeciess ON AkcesPodrobnosti.SubSpeciesAutoID =
TableOfSubSpeciess.SubSpeciesAutoID) LEFT JOIN TableOfCountrys ON
AkcesPodrobnosti.CountryAutoID = TableOfCountrys.CountryAutoID) ON
Akces.AkcesAutoID = AkcesPodrobnosti.AkcesAutoID
ORDER BY Akces.Rok, Akces.Akcesit, AkcesPodrobnosti.EvidenceLetter,
AkcesPodrobnosti.EvidenceNumber, AkcesPodrobnosti.EvidenceExtra;