Just so I understand, are you suggesting that I remove the Grade from the
Eemployee table, create a seperate table, and link this new table to the OT
table via the EmployeeID field? I foreget to inlcude in my original post
that the overtime hourly rate is dependent on the Grade - would you recommend
storing this value in this new table as well?
Yes and yes.
Here is a suggested structure. I've made numerous assumptions about
business rules so it's just an example for ideas rather than a working
solution.
To actually run the code, execute each statement individually against
the OLE DB provider for Jet 4.0, either by putting Access into
'ANSI-92' mode
(
http://office.microsoft.com/en-us/assistance/HP030704831033.aspx) or
using code (e.g. in Access: CurrentProject.Connection.Execute "SQL
statement here"):
CREATE TABLE Employee (
EmployeeID INTEGER NOT NULL PRIMARY KEY
)
;
CREATE TABLE Grades (
grade CHAR(3) NOT NULL PRIMARY KEY,
CHECK(grade LIKE '[1-9].[0-9]')
)
;
CREATE TABLE EmployeesGrades (
EmployeeID INTEGER NOT NULL
REFERENCES Employee (EmployeeID)
ON DELETE CASCADE
ON UPDATE NO ACTION,
position_grade CHAR(3) NOT NULL
REFERENCES Grades (grade)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
start_date DATETIME NOT NULL,
end_date DATETIME,
CHECK(start_date <= end_date),
PRIMARY KEY (position_grade, start_date, EmployeeID)
)
;
CREATE VIEW EmployeesCurrentGrades
(EmployeeID, position_grade)
AS
SELECT T1.EmployeeID, T1.position_grade
FROM EmployeesGrades AS T1
WHERE T1.start_date = (
SELECT MAX(start_date)
FROM EmployeesGrades
WHERE EmployeeID = T1.EmployeeID
AND end_date IS NULL)
;
CREATE TABLE Overtime (
EmployeeID INTEGER NOT NULL
REFERENCES Employee (EmployeeID)
ON DELETE CASCADE
ON UPDATE NO ACTION,
start_date DATETIME NOT NULL,
end_date DATETIME,
CHECK(start_date < end_date),
CHECK(DATEDIFF('h', start_date, end_date) <= 5),
PRIMARY KEY (EmployeeID, start_date)
)
;
CREATE TABLE OvertimeRates (
position_grade CHAR(3) NOT NULL
REFERENCES Grades (grade)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
overtime_rate DECIMAL(8, 4) NOT NULL,
CHECK(overtime_rate >= 0),
start_date DATETIME NOT NULL,
end_date DATETIME,
CHECK(start_date <= end_date),
PRIMARY KEY (position_grade, start_date)
)
;
CREATE VIEW OvertimeCurrentRates (
position_grade, overtime_rate)
AS
SELECT T1.position_grade, T1.overtime_rate
FROM OvertimeRates AS T1
WHERE T1.start_date = (
SELECT MAX(start_date)
FROM OvertimeRates
WHERE position_grade = T1.position_grade
AND end_date IS NULL)
;
CREATE VIEW OvertimeGrades
(EmployeeID, overtime_start_date, overtime_end_date, position_grade)
AS
SELECT Overtime.EmployeeID, Overtime.start_date, Overtime.end_date,
EmployeesGrades.position_grade
FROM Overtime LEFT JOIN EmployeesGrades
ON Overtime.EmployeeID = EmployeesGrades.EmployeeID
AND (Overtime.start_date BETWEEN EmployeesGrades.start_date AND
IIF(EmployeesGrades.end_date IS NULL, NOW(), EmployeesGrades.end_date))
;
CREATE VIEW OvertimeDetails
(EmployeeID, overtime_start_date, overtime_end_date, position_grade,
overtime_rate)
AS
SELECT OvertimeGrades.EmployeeID, OvertimeGrades.overtime_start_date,
OvertimeGrades.overtime_end_date, OvertimeGrades.position_grade,
OvertimeRates.overtime_rate
FROM OvertimeGrades LEFT JOIN OvertimeRates
ON OvertimeGrades.position_grade = OvertimeRates.position_grade
AND (OvertimeGrades.overtime_start_date BETWEEN
OvertimeRates.start_date AND IIF(OvertimeRates.end_date IS NULL, NOW(),
OvertimeRates.end_date))
;
INSERT INTO Employee (EmployeeID) VALUES (1)
;
INSERT INTO Employee (EmployeeID) VALUES (2)
;
INSERT INTO Grades (grade) VALUES ('5.1')
;
INSERT INTO Grades (grade) VALUES ('5.2')
;
INSERT INTO Grades (grade) VALUES ('7.1')
;
INSERT INTO OvertimeRates (position_grade, overtime_rate, start_date,
end_date) VALUES ('5.1', 100, #2004-01-01#, #2004-12-31#)
;
INSERT INTO OvertimeRates (position_grade, overtime_rate, start_date,
end_date) VALUES ('5.2', 120, #2004-01-01#, #2004-12-31#)
;
INSERT INTO OvertimeRates (position_grade, overtime_rate, start_date,
end_date) VALUES ('7.1', 200, #2004-01-01#, #2004-12-31#)
;
INSERT INTO OvertimeRates (position_grade, overtime_rate, start_date)
VALUES ('5.1', 110, #2005-01-01#)
;
INSERT INTO OvertimeRates (position_grade, overtime_rate, start_date)
VALUES ('5.2', 150, #2005-01-01#)
;
INSERT INTO OvertimeRates (position_grade, overtime_rate, start_date)
VALUES ('7.1', 210, #2005-01-01#)
;
INSERT INTO EmployeesGrades (EmployeeID, position_grade, start_date,
end_date) VALUES (1,'5.1',#2003-09-01#,#2005-03-31#)
;
INSERT INTO EmployeesGrades (EmployeeID, position_grade, start_date)
VALUES (1,'5.2',#2005-04-01#)
;
INSERT INTO EmployeesGrades (EmployeeID, position_grade, start_date,
end_date) VALUES (2,'5.2',#2004-04-30#,#2004-11-30#)
;
INSERT INTO EmployeesGrades (EmployeeID, position_grade, start_date)
VALUES (2,'7.1',#2004-12-01#)
;
INSERT INTO Overtime (EmployeeID, start_date, end_date) VALUES (1,
#2004-10-01 17:00:00#, #2004-10-01 20:00:00#)
;
INSERT INTO Overtime (EmployeeID, start_date, end_date) VALUES (2,
#2004-10-01 17:00:00#, #2004-10-01 20:00:00#)
;
INSERT INTO Overtime (EmployeeID, start_date, end_date) VALUES (1,
#2005-10-01 17:00:00#, #2005-10-01 20:00:00#)
;
INSERT INTO Overtime (EmployeeID, start_date, end_date) VALUES (2,
#2005-10-01 17:00:00#, #2005-10-01 20:00:00#)
;
SELECT EmployeeID, overtime_start_date, overtime_end_date,
position_grade, overtime_rate
FROM OvertimeDetails
ORDER BY overtime_start_date, position_grade
;