Taking your outstanding points one by one:
a) The "Bill-Bowling-1/1/09-Home" duplication thing when I run the
query.
With your present tblTaskDo design you really have no alternative but to
enter one row per date. Otherwise you have no way of knowing when the task
was completed, only when started. You could avoid this by changing the table
design so that it has DateStarted and DateFinished columns. You'd then be
defining the range of dates over which the task was undertaken. If it takes
one day both dates would be the same. This does make querying by a specific
date a little more complex, however, as the date may be in the middle of a 3
or more day range. Its not too difficult to cope with this as you simply
query for rows:
WHERE [Enter date:] BETWEEN DateStarted AND DateFinished
where [Enter date:] is a parameter for which the user enters a value when
prompted at runtime.
It gets a lot trickier, however, if you are querying by a date range as the
query then has to look for intersecting ranges. The logic for this is:
(a) start date within parameter range, or
(b) end date within parameter range, or
(c) parameter range entirely within data range
which can be directly done with an expression in a query's WHERE clause, but
I use the following function:
Public Function WithinRange(dtmParamRangeStart As Date, _
dtmParamRangeEnd As Date, _
dtmDataRangeStart As Date, _
dtmDataRangeEnd As Date) As Boolean
' Accepts: date at which parameter range starts
' date at which parameter range ends
' date at which data range starts
' date at which data range ends
' Returns: True if data range intersects with parameter range
' False if whole of data range outside parameter range
' return True if:
' (a) start date within parameter range, or
' (b) end date within parameter range, or
' (c) parameter range entirely within data range
WithinRange = _
(dtmDataRangeStart >= dtmParamRangeStart And _
dtmDataRangeStart <= dtmParamRangeEnd) _
Or (dtmDataRangeEnd >= dtmParamRangeStart And _
dtmDataRangeEnd <= dtmParamRangeEnd) _
Or (dtmDataRangeStart <= dtmParamRangeStart And _
dtmDataRangeEnd >= dtmParamRangeEnd)
End Function
and call it in the query like so:
WHERE WithinRange([Enter start date:], [Enter end date:], DateStarted,
DateFinished)
[Enter start date:] and [Enter end date:] being the parameters for which the
user enters values at runtime.
If you stick with the current table design with the single TaskDate column
you can return just one row per employee/task with for example the date they
started the task, by grouping the query by employee, location and task and
then returning the Min(TaskDate). The problem with this, though, is that it
would return only the very first day an employee started a particular task at
a particular location, even if they had undertaken that task on separate
occasions at the same location many moths or even years apart, which might
not be what you want. There are ways in which you could return the Min
(TaskDate) for each subset of the employee/location/task grouping where each
subset of dates are a continuous series, but that's getting into some quite
advanced querying techniques, and I think I've probably given you more than
enough to chew on already.
Looking at the problem from another angle you can regard the query as the
intermediary between the raw data and the final form of presentation of the
results as a report. The duplication in the query's results is irrelevant
therefore, as you can control the format in which the results are presented
in the report by grouping the report by employee, location and task and
putting this data in a group header. All the dates per
employee/location/task would then normally be listed in the detail section,
but it would be possible to suppress all but the first data in any continuous
series of dates, ignoring weekends and holidays if necessary, by writing code
in the report's module. I realise that this, and much of what I've said
above, will be beyond your current experience level, but there is no getting
away from the fact that Access does present a learning curve which is not
trivial if these sort of things are to be tackled.
b) The whole indexes thing. That went right over my head. What fields
from what tables do I create the index on? Would that stop the
duplicate events thing?
Indexes serve two purposes (a) they speed up performance (b) in the case of
'unique' indexes (no duplicates) they protect the integrity of the data by
preventing two or more rows in a table from having the same value or values
in a column or set of columns where this would be incorrect. In the case of
your tblTaskDo table it would be incorrect to have the same values in all
three of the TaskIDfk, TaskDate and TaskLocation columns in more than one
row, so a unique index should be created on these three columns in the way I
described.
c) Do I check the ref/cascade/cascade? Is Join Type 1 OK?
You should certainly check the 'enforce referential integrity' check box in
all relationships as this (a) prevents unmatched rows being entered into
related tables and (b) prevents a rows in one table being deleted if it would
leave unmatched rows in a related table.
As you are using autonumbers as the primary keys there is no point in
enforcing cascade updates as an autonumber column's value is automatic can't
be changed. You'd only enforce cascade updates if a 'natural' key was used,
where you might change a value in the primary key column of a table, and want
the values in the relevant foreign key columns in other tables to be
automatically changed to the new value in all matching rows.
Enforcing cascade deletes is something which requires careful consideration
as it means that if you delete a row in a table n the 'one' side of the
relationship all matching rows in the table on the 'many' side will be
automatically deleted. Sometimes this is appropriate, sometimes not. If in
doubt as to the wisdom of doing it, then don't. You'd then be unable to
delete a row in a table n the 'one' side of the relationship until all
matching rows in the table on the 'many' side have been deleted, so you
cannot leave unmatched rows in the 'many' side table.
Ken Sheridan
Stafford, England