First, regarding your question, the way to solve this problem from the point of view of the circuit is to keep the previous timeout for each row. The trick for this project is a unique constraint on ProjectNumber, EmployeeId and DateTimeIn and a foreign key reference for ProjectNumber, EmployeeId and PreviousDateTime for ProjectNumber, EmployeeId, DateTimeOut. It should also be noted that I rely on unique constraints that allow a single null, which is not true in all database systems (of course, I also rely on the database system, observing control restrictions, which is also not true for all database systems).
Create Table Punches ( PunchId int not null Primary Key , ProjectNumber varchar... , EmployeeId int not null.. , DateTimeIn datetime not null , DateTimeOut datetime null , PreviousDateTimeOut datetime null , Unique ( ProjectNumber, EmployeeId, DateTimeIn ) , Unique ( ProjectNumber, EmployeeId, DateTimeOut ) , Unique ( ProjectNumber, EmployeeId, PreviousDateTimeOut ) , Check ( DateTimeIn <= DateTimeOut ) , Check( PreviousDateTimeOut <= DateTimeIn ) , Foreign Key ( ProjectNumber, EmployeeId, PreviousDateTimeOut ) References Punches( ProjectNumber, EmployeeId, DateTimeOut ) )
The surface of this approach is that the circuit itself prevents overlapping. The downside is that inserts are a bit more complicated. The first row for this employee for this project will have to use the null value for PreviousDateTimeOut, since we will not have the previous row. Secondly, this means that for punching you will need to find the previous datetime
Insert Punches( ProjectNumber, EmployeeId, DateTimeIn, PreviousDateTimeOut ) Select ProjectNumber, EmployeeId, CURRENT_TIMESTAMP , Coalesce( ( Select Max( DateTimeOut ) From Punches Where DateTimeOut Not Null ) , CURRENT_TIMESTAMP )
The above just solves the overlap problem. However, this does not necessarily solve the problem of a forgotten hit, except to prevent punching without previous punching, preventing the insertion of two rows for the same ProjectNumber and Employee and null DateTimeOut. What should happen may include more complex business rules.