这是一种对打卡时间进行排序的方法,然后使用递归 CTE 自连接以将时间缝合在一起。像这样的系统很难控制错过的出拳等,但我尝试向您展示一种方法,您可以通过添加 HoursWorked 阈值来做到这一点。
/* Create some sample Employee punch data to test*/
IF OBJECT_ID('tempdb..#AttLogTest') IS NOT NULL
DROP TABLE #AttLogTest
CREATE TABLE #AttLogTest (EnrollNumber INT NOT NULL, PunchTime DATETIME NOT NULL)
INSERT INTO #AttLogTest (EnrollNumber, PunchTime)
SELECT 10, '2015-08-01 08:01:03' UNION ALL
SELECT 10, '2015-08-02 07:57:35' UNION ALL
SELECT 10, '2015-08-01 16:15:23' UNION ALL
SELECT 10, '2015-08-02 16:17:46' UNION ALL
SELECT 12, '2015-08-01 21:59:31' UNION ALL
SELECT 12, '2015-08-02 05:59:02' UNION ALL
SELECT 12, '2015-08-02 22:02:28' UNION ALL
SELECT 12, '2015-08-03 06:01:24' UNION ALL
SELECT 14, '2015-08-01 07:59:01' UNION ALL
SELECT 14, '2015-08-02 07:58:16' UNION ALL
SELECT 14, '2015-08-02 16:02:48'
/* Employee time query logic below */
/* First, create a temp table that sequences the punch times for each employee */
IF OBJECT_ID('tempdb..#EmployeeTimeSequence') IS NOT NULL
DROP TABLE #EmployeeTimeSequence
SELECT
EnrollNumber
,PunchTime
,PunchSequence = ROW_NUMBER() OVER(PARTITION BY EnrollNumber ORDER BY PunchTime)
INTO #EmployeeTimeSequence
FROM #AttLogTest --Replace this with your dbo.AttLog table if this solution works for you
/*WHERE clause could be added here to filter for specific dates or EnrollNumbers */
/* If time between punches is greater than this threashold, then it will be treated as a missed punch
in logic below. Remove this or modify as needed. */
DECLARE @MissedPunchThreshold int
SET @MissedPunchThreshold = 20
/* Next, create a recursive CTE which will stitch together the punch times and ensure punch times don't overlap when
self-joining to #EmployeeTimeSequence. */
;WITH EmployeeTimeCTE (EnrollNumber, CheckDate, Time_In, Time_Out, HoursBetweenPunch, PunchOutSequence)
AS (
/* Anchor member */
SELECT
ETS_In.EnrollNumber
,CAST(ETS_In.PunchTime AS DATE) AS CheckDate
,ETS_In.PunchTime AS Time_In
,ETS_Out.PunchTime AS Time_Out
,DateDiff(hour, ETS_In.PunchTime, ETS_Out.PunchTime) AS HoursBetweenPunch
,ETS_Out.PunchSequence AS PunchOutSequence
FROM #EmployeeTimeSequence AS ETS_In
LEFT OUTER JOIN #EmployeeTimeSequence AS ETS_Out
ON ETS_In.EnrollNumber = ETS_Out.EnrollNumber
AND ETS_Out.PunchSequence = ETS_In.PunchSequence + 1
WHERE ETS_In.PunchSequence = 1
UNION ALL
/* Recursive memebr - build on top of anchor */
SELECT
ETS_In.EnrollNumber
,CAST(ETS_In.PunchTime AS DATE) AS CheckDate
,ETS_In.PunchTime AS Time_In
,ETS_Out.PunchTime AS Time_Out
,DateDiff(hour, ETS_In.PunchTime, ETS_Out.PunchTime) AS HoursBetweenPunch
,ETS_Out.PunchSequence AS PunchOutSequence
FROM #EmployeeTimeSequence AS ETS_In --get the time for the in punch
INNER JOIN EmployeeTimeCTE ET
ON ET.EnrollNumber = ETS_In.EnrollNumber
AND ETS_In.PunchSequence =
CASE
WHEN ET.HoursBetweenPunch > @MissedPunchThreshold -- if more than threshold, then treat as missed punch
THEN ET.PunchOutSequence -- then treat the previous out punch as the next in punch instead
ELSE ET.PunchOutSequence + 1 -- else join as usual to get the next punch in sequence
END
INNER JOIN #EmployeeTimeSequence AS ETS_Out -- get the time for the out punch
ON ETS_In.EnrollNumber = ETS_Out.EnrollNumber
AND ETS_Out.PunchSequence = ETS_In.PunchSequence + 1
)
/* Now query the CTE */
SELECT
EnrollNumber AS EmpID
,CheckDate
,Time_In
,CASE WHEN HoursBetweenPunch > @MissedPunchThreshold THEN NULL ELSE Time_Out END AS Time_Out
,CASE WHEN HoursBetweenPunch > @MissedPunchThreshold THEN NULL ELSE HoursBetweenPunch END AS HoursBetweenPunch
FROM EmployeeTimeCTE
ORDER BY EnrollNumber, CheckDate
OPTION (MAXRECURSION 1000)