【问题标题】:SQL - Categorize employee entrance timeSQL - 对员工入职时间进行分类
【发布时间】:2014-04-27 16:59:05
【问题描述】:

我有一个 SQL 表,用于存储每个员工的进出时间戳。

有员工不止一次打卡(因为他们没有听到卡片被读取),其他员工出于某种原因打卡(也许他们遇到朋友和保持谈话 1 或 2 分钟)他们会等几分钟,然后再次打卡进入旋转栅门。

示例 SQL 表数据如下所示(仅限 3 名员工):

现在,我想对每个时间进行分类,以了解哪个时间戳是正确的,等等。

例如:

如您所见,EMP_01(黄色记录)从技术上讲,LAST_LEAVE 是 00:38:21,但很明显,员工打了两次拳,这就是为什么真正的 LAST_LEAVE 是 00:38:16。

但是,EMP_04还有一些其他的问题,如果你看最后4条记录,他连续有3条IN,但这不是因为他没有听到传感器的哔哔声,而是因为他从另一扇门离开没有拳头谁知道什么时候,但我们知道他在 1 小时后再次进入,依此类推。

那么,关于如何处理这个问题有什么建议吗?以及如何分配我的“状态”标志?

更新

如果 EMP_04 有 3 个相同类型(IN 或 OUT)的连续时间戳,例如:

  • 上午 9:04:27(印第安纳州)
  • 上午 9:04:35(印第安纳州)
  • 上午 9:04:40(印第安纳州)

两次之间我的容忍度是 120 秒,所以第一次 9:04:27 是可以的,但是接下来的 2 个连续时间戳应该是 INACTIVE 因为我的容忍度是 120 秒。

但是,例如,如果 EMP_04 具有以下连续时间戳:

  • 上午 9:04:27(印第安纳州)
  • 上午 9:04:35(印第安纳州)
  • 上午 9:08:10(印第安纳州)

那么,唯一标记为 INACTIVE 的时间将是上午 9:04:35。因为上午 9:04.35 到 9:08:10 之间的时间大于 2 分钟。

【问题讨论】:

  • 您使用的是什么数据库?而且,您能否更明确地说明您要查找的不同组的规则?
  • SQL 2012,是的,我想将重复的时间(可能有 2 分钟的容差)标记为 INACTIVE,所以如果员工有 3 个连续的 IN,那么第一个 IN 不会被标记但接下来的 2 个时间戳,如果它们之间的秒数或分钟数少于 2 分钟,则标记为 INACTIVE。
  • 我只是更新问题。
  • 我仍然没有得到不活动的规则。时间戳 9:04:35am // 将被标记为非活动,因为它在前一个时间戳 9:04:27am 的时间范围(2 分钟)内 //
  • +1 以良好的格式详细询问

标签: c# sql sql-server-2012


【解决方案1】:

首先,感谢@RamRS 将辛勤工作投入到触发器中,这是经过测试的,应该让你开始。很明显,极端情况需要一些可靠的测试:

CREATE TRIGGER TS_FindDups ON dbo.TS FOR INSERT
AS
BEGIN

;WITH DupRecords
AS
(
        SELECT i.EmployeeId, i.[TimeStamp]
          FROM inserted i
    INNER JOIN dbo.TS SRC ON i.EmployeeId = SRC.EmployeeId AND i.EntranceType = SRC.EntranceType
         WHERE SRC.[Status] != 'Inactive'
               /* same day (nice...thanks to RamRS) */
               AND CAST(i.[TimeStamp] AS date) =  CAST(SRC.[TimeStamp] AS date)
               /* only checking newer timestamps (also need this so DATEDIFF <= 2 works correctly) */
               AND i.[TimeStamp] > SRC.[TimeStamp]
               /* newer timestamps are less than two minutes for same Employee, same day, same EntranceType */
               AND DATEDIFF(MINUTE, SRC.[TimeStamp], i.[TimeStamp]) <= 2
)

      UPDATE dbo.TS
         SET [Status] = 'Inactive'
        FROM dbo.TS SRC
  INNER JOIN DupRecords ON SRC.EmployeeId = DupRecords.EmployeeId AND SRC.[TimeStamp] = DupRecords.[TimeStamp]

END

【讨论】:

  • CTE 推动的单一声明 UPDATE。并经过测试。考虑一下我的想法正式吹响了!
【解决方案2】:

就像@mdisibio 所说,我们可以使用触发器在插入行时将值“INACTIVE”添加到重复行。

这个触发器的粗略草图是:

CREATE TRIGGER trg_FindDups ON tbl_TimeStamp FOR INSERT
AS
BEGIN
    DECLARE @CurrentMaxSwipeTime DATETIME
    DECLARE @SwipeType
    SELECT @SwipeType = EntranceType FROM INSERTED

    IF (@SwipeType = 'O')
    BEGIN
        /* Employee is swiping OUT */
        SELECT @CurrentMaxSwipeTime = MAX(T.TimeStamp) FROM tbl_TimeStamp T 
        WHERE T.EmployeeID = (SELECT EmployeeID FROM INSERTED) /* Same employee */
        AND DATE(T.TimeStamp) = CAST(GETDATE() AS DATE) /* Same date */
        AND DATEDIFF(minute,T.TimeStamp,GETDATE())<2 /* Within 2 minutes */
        AND T.Status <> 'INACTIVE' /* of an active swipe */
        AND T.EntraceType = 'O' /* Swiping out */
        AND T.TimeStamp <> (SELECT TimeStamp FROM INSERTED) /* not the row we just inserted, H/T @mdisibio
        IF @CurrentMaxSwipeTime IS NOT NULL
            /* SET Status to INACTIVE */
            UPDATE tbl_TimeStamp SET Status='INACTIVE' WHERE EmployeeID=(SELECT EmployeeID FROM INSERTED) AND TimeStamp=(SELECT TimeStamp FROM INSERTED)
    END

    ELSE
    BEGIN
        /* Employee is swiping in */
        SELECT @CurrentMaxSwipeTime = MIN(T.TimeStamp) FROM tbl_TimeStamp T 
        WHERE T.EmployeeID = (SELECT EmployeeID FROM INSERTED) /* Same employee */
        AND DATE(T.TimeStamp) = CAST(GETDATE() AS DATE) /* Same date */
        AND DATEDIFF(minute,T.TimeStamp,GETDATE())<2 /* Within 2 minutes */
        AND T.Status <> 'INACTIVE' /* of an active swipe */
        AND T.EntraceType = 'I' /* Swiping in */
        AND T.TimeStamp <> (SELECT TimeStamp FROM INSERTED) /* not the row we just inserted, H/T @mdisibio
        IF @CurrentMaxSwipeTime IS NOT NULL
            /* SET Status to INACTIVE */
            UPDATE tbl_TimeStamp SET Status='INACTIVE' WHERE EmployeeID=(SELECT EmployeeID FROM INSERTED) AND TimeStamp=(SELECT TimeStamp FROM INSERTED)
    END
END

我在 SQL Server 上工作已经有几年了,但我真的希望代码中没有错误。

--
干杯,
拉姆

【讨论】:

  • @VAAA 抱歉,到目前为止,我错过了您对触发器的兴趣。 @RAMRS 感谢您提出此触发器的建议。两个cmets:1)如果要忽略该行,我会更多地考虑ROLLBACK。取决于 VAAA 是否真的需要保存行,或者“非活动”是否是一种解决方法。 2)我需要运行一个测试用例来确定,但我认为这个触发器将始终与新插入的记录匹配,因为它们已经存在于 tbl_TimeStamp 中。我(或其他任何人)将研究替代方案。
  • 我是否应该添加一个子句来排除当前记录?
  • 另外,我没有建议ROLLBACK 以避免任何数据丢失。
  • 努力。不幸的是,INSERTED 是一个表,所以某些连接语法不正确...
  • 我认为他的意思是你不使用回滚的决定是好的,并鼓励你不要听从我的特别建议:)
【解决方案3】:

让我快速总结您的问题并一一解决:

  1. 误刷两次时重复 OUT 记录。状态应该如何设置?
  2. 复制 IN 记录,因为并非所有门都已滑动且不记录相应的 OUT 记录。又该如何设置状态?

假设:

  1. 如果人们误扫描多次,您可能会得到重复的IN和OUT记录。
  2. 您不会在目前没有卡片扫描仪的门上安装卡片扫描仪...
  3. 这是一个不完美的系统,因此您不会删除\忽略不同场景中的任何扫描。

所以... 复制中间有几分钟的记录。为了确定用户的状态,您必须查看他们的历史记录。我相信你的容忍几分钟的想法是一个很好的想法,尽管这应该在 IN 和 OUT 扫描中检查。对于 IN,您应该始终拿第一个。对于 OUT,您应该始终选择最后一个。

我的想法是,每次您为特定员工插入/更新/删除特定日期的时间戳记录时,都会重新计算他​​们当天的所有状态。这样一来,如果存在重复扫描,您就不必担心在不同行上来回调整状态。

您将不得不重新计算以下状态:

  1. FIRST_ENTRANCE - 在特定日期具有 EntranceType I 的员工的第一个时间戳行
  2. LAST_LEAVE - 在特定的 EntranceType O 上的员工的最后时间戳行 所有员工的一天。在设置此之前,请检查当天所有其他员工在同一天没有此。
  3. FIRST_IN - 具有 EntranceType I 的特定员工的第一个时间戳行 所有员工的一天。在设置此之前,请检查当天所有其他员工在同一天没有此。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-17
    相关资源
    最近更新 更多