Marshall has assumed that your Absences table is incorrectly designed, which may or may not be the case. If you do have separate columns (fields) for each type of absence then that is a bad design as it encodes data as column headings. A fundamental
principle of the database relational model is the Information Principle. This requires that all data be stored as values at column positions in rows in tables, and in no other way.
What you have is a many-to-many relationship type between employees and absence types. A correct design would have three tables, e.g.
Employees
....EmployeeID (PK)
....FirstName
....LastName
....etc
AbsenceTypes
....AbsenceType (PK)
The latter would have one row for sickness, one for vacation etc. To model the many-to-many relationship type between the two you'd have a third table which resolves the many-to-many relationship type into two one-to-many relationship types:
EmployeeAbsences
....EmployeeID (FK)
....AbsenceType (FK)
....DateFrom
....DateTo
The one-to-many relationships between Employees and EmployeeAbsences on EmployeeID, and between AbsenceTypes and EmployeeAbsences on AbsenceType should be enforced.
A query to return the total each absence type per employee would sum the difference between the DateTo and DateFrom times, but this does not allow for weekends or public/concessionary holidays. To discount these the following function can be used:
Public Function WorkDaysDiff(varLastDate As Variant, _
varFirstDate As Variant, _
Optional blnExcludePubHols As Boolean = False) As Variant
Const conSATURDAY As Integer = 6
Const conSUNDAY As Integer = 7
Dim lngDaysDiff As Long, lngWeekendDays As Long
Dim intPubHols As Integer
If IsNull(varLastDate) Or IsNull(varFirstDate) Then
Exit Function
End If
' if first date is Sat or Sun start on following Monday
Select Case WeekDay(varFirstDate, vbMonday)
Case conSATURDAY
varFirstDate = varFirstDate + 2
Case conSUNDAY
varFirstDate = varFirstDate + 1
End Select
' if last date is Sat or Sun finish on following Monday
Select Case WeekDay(varLastDate, vbMonday)
Case conSATURDAY
varLastDate = varLastDate + 2
Case conSUNDAY
varLastDate = varLastDate + 1
End Select
' get total date difference in days
lngDaysDiff = DateDiff("d", varFirstDate, varLastDate)
' get date difference in weeks and multiply by 2
' to get number of weekend days
lngWeekendDays = DateDiff("ww", varFirstDate, varLastDate, vbMonday) * 2
' subtract number of weekend days from total date difference
' to return number of working days
WorkDaysDiff = lngDaysDiff - lngWeekendDays
' exclude public holidays if required
If blnExcludePubHols Then
intPubHols = DCount("*", "PubHols", "HolDate Between #" _
& Format(varFirstDate, "yyyy-mm-dd") & "# And #" & _
Format(varLastDate - 1, "yyyy-mm-dd") & "#")
WorkDaysDiff = WorkDaysDiff - intPubHols
End If
End Function
This requires a table named PubHols with a column HolDate in which each public/concessionary holiday date is entered. A query to return the total absences per employee per absence type, discounting public/concessionary holidays, would be:
SELECT EmployeeID, AbsenceType,
SUM(WorkDaysDiff(DateTo, DateFrom, TRUE)) AS DaysAbsent
FROM EmployeeAbsences
GROUP BY EmployeeID, AbsenceType;
You can create a form in continuous forms view based on this query and embed it as a subform within your employees form, linked to the parent form on EmployeeID as Marshall described.
It gets a little more complex if you need to return separate values for different date ranges. In my own organisation our leave year ran from 1 April to 31 March each year, which was our standard accounting year for leave allowances as well as financially,
so it would be appropriate to return the totals per accounting year. For this another table can be created:
AccountingYears
....AccountingYear
....StartDate
....EndDate
so for the last two years in our case this would have rows:
2011-12 2011-04-01 2012-03-31
2012-13 2012-04-01 2013-03-31
I've used the ISO standard of YYYY-MM-DD for date notation here, but you'd normally enter the dates in your local regional format of course. If we assume that no period of absence spans the boundary between accounting years then a query to return the absences
per employee per absence type per accounting year would be:
SELECT EmployeeID, AccountingYear, AbsenceType,
SUM(WorkDaysDiff(DateTo, DateFrom, TRUE)) AS DaysAbsent
FROM EmployeeAbsences INNER JOIN AccountingYears
ON EmployeeAbsences.DateFrom >= AccountingYears.StartDate
AND EmployeeAbsences.DateFrom <= AccountingYears.EndDate
GROUP BY AccountingYear, EmployeeID, AbsenceType;
If a period of absence could span the boundary between accounting years it gets more complex, as it is then necessary to detect intersecting ranges. This in itself is not difficult, but it also becomes necessary to compute how many days of the period of absence
fall with each accounting year. It's simpler if in such a case the period of absence is entered in the EmpoyeeAbsences table as two rows, e.g. if employee 42 is sick from 28th March to 4th April:
42 Sickness 2012-03-28 2012-03-30
42 Sickness 2012-04-02 2012-04-04
The 31st march and 1st April were the weekend this year, so don't appear in the ranges, but even if they did the WorkDaysDiff function would discount them.