修复记录重复时交叉应用行

Fix cross applying rows when records are duplicated

我有一个 EVENT table,其中包含进入和退出事件。我用这个程序计算工作时间:

SET @worktime = (SELECT SUM(mins)
                 FROM
                     (SELECT
                          entry.EmployeeId, entry.DateTime AS EntryDateTime, 
                          [exit].DateTime AS ExitDateTime, DATEDIFF(MINUTE, entry.DateTime, [exit].DateTime) AS mins
                      FROM
                          Events entry
                      CROSS APPLY 
                          (SELECT TOP 1 e.DateTime
                           FROM Events e
                           WHERE e.EmployeeId = entry.EmployeeId
                             AND e.DateTime > entry.DateTime
                             AND e.EventTypeID = 2
                             AND CAST(e.DateTime AS DATE) = CAST(@data AS DATE)
                             AND e.ControlPointID IN (SELECT ControlPointID 
                                                      FROM ControlPoints 
                                                      INNER JOIN dbo.Split(RIGHT(@rcp, LEN(@rcp) - 2), ';') AS split ON ControlPoints.NAme = split.Data + ' EXIT')
                           ORDER BY e.DateTime ASC) AS [exit]
                      WHERE 
                          entry.EventTypeId = 1 
                          AND EmployeeId = @code 
                          AND CAST(entry.DateTime AS DATE) = CAST(@data AS DATE) 
                          AND ControlPointID IN (SELECT ControlPointID 
                                                 FROM ControlPoints 
                                                 INNER JOIN dbo.Split(RIGHT(@rcp, LEN(@rcp) - 2), ';') AS split ON ControlPoints.NAme = split.Data + ' ENTRY') 
                                                                                                                AND CAST(entry.[DateTime] AS DATE) = CAST(@data AS DATE)) AS input
                 GROUP BY EmployeeId)

例如,某些员工有两个进入和两个退出事件,它工作得很好。但是,如果有一个退出事件和两个进入事件,它就不会像我想的那样工作。

示例:员工 XXX 在 05:44 上班,然后在 06:28 下班。稍后他在 06:50 返回并在 12:33 再次退出。这个程序应该return@worktime等于387分钟。

第二天他犯了一个错误,他来到06:00上班,但是在EVENTStable,有两条相同的记录。他在 14:00 退出(EVENT table 中只有 1 exit 条记录)。程序 returns 960 分钟。我想 returns 480.

当进入事件的数量不等于退出事件的数量(例如:2 次进入和 1 次退出)时,我该如何修复交叉应用?

图片示例:

下面的例子很完美。

选定的行 (1,4,5,8) 已传递给过程。 ControlPointId = 6 是入口点,ControlPointId = 3 是出口点。 EventTypeId = 1 是进入事件,EventTypeId = 2 是退出事件。

但这不起作用:

在这种情况下,ControlPointId = 64 是入口点,ControlPointId = 56 是出口点。我们可以看到,这个Employee犯了错误,ControlPoint读了他的卡片两次,所以有2个进入事件和1个退出事件。

我应该如何更改程序?当出现这样的错误时(或相反的情况:1 次进入和 2 次退出事件),应该只需要第一个进入事件。

你可以试试这个。如果不同的进入事件有相同的退出事件,我使用 ROW_NUMBER

保留第一个进入
SET @worktime = (SELECT SUM(mins)
                 FROM
                     (SELECT
                          entry.EmployeeId, entry.DateTime AS EntryDateTime, 
                          [exit].DateTime AS ExitDateTime, 
                          DATEDIFF(MINUTE, entry.DateTime, [exit].DateTime) AS mins,
                          RN = ROW_NUMBER () OVER (PARTITION BY [exit].EventID ORDER  BY entry.DateTime)
                      FROM
                          Events entry
                      CROSS APPLY 
                          (SELECT TOP 1 e.DateTime, e.EventID
                           FROM Events e
                           WHERE e.EmployeeId = entry.EmployeeId
                             AND e.DateTime > entry.DateTime
                             AND e.EventTypeID = 2
                             AND CAST(e.DateTime AS DATE) = CAST(@data AS DATE)
                             AND e.ControlPointID IN (SELECT ControlPointID 
                                                      FROM ControlPoints 
                                                      INNER JOIN dbo.Split(RIGHT(@rcp, LEN(@rcp) - 2), ';') AS split ON ControlPoints.NAme = split.Data + ' EXIT')
                           ORDER BY e.DateTime ASC) AS [exit]
                      WHERE 
                          entry.EventTypeId = 1 
                          AND EmployeeId = @code 
                          AND CAST(entry.DateTime AS DATE) = CAST(@data AS DATE) 
                          AND ControlPointID IN (SELECT ControlPointID 
                                                 FROM ControlPoints 
                                                 INNER JOIN dbo.Split(RIGHT(@rcp, LEN(@rcp) - 2), ';') AS split ON ControlPoints.NAme = split.Data + ' ENTRY') 
                                                                                                                AND CAST(entry.[DateTime] AS DATE) = CAST(@data AS DATE)) AS input
                 WHERE RN = 1
                 GROUP BY EmployeeId)