【问题标题】:Calculate time difference for attendance计算出勤时差
【发布时间】:2014-01-19 21:36:11
【问题描述】:

我有一个包含以下示例输出的表格。

UserID  Checktime              CheckStatus
3175    2013-12-22 07:02:10.000     I
3175    2013-12-22 13:01:01.000     O
3175    2013-12-22 13:49:54.000     I
3175    2013-12-22 13:49:55.000     I
3175    2013-12-22 15:58:42.000     O
3175    2013-12-23 06:02:58.000     I
3175    2013-12-23 14:00:29.000     O
3175    2013-12-24 05:17:09.000     I
3175    2013-12-24 12:34:25.000     O
3175    2013-12-24 12:34:26.000     O

我想构建一个查询来实现以下结果:

UserID  Date       CheckIn   CheckOut Hours
3175    2013-12-22 07:02:10  13:01:0  5.98
3175    2013-12-22 13:49:54  15:58:42 2.15

注意: 1.重复的IN被忽略。原始数据中的第三和第四行。 2. 小时计算时,分钟为小时的小数点。

我需要 tsql 查询的帮助来获得这些结果。

我当前的代码导致了许多其他问题 - 因为它每次都必须在临时表中重新计算。

【问题讨论】:

  • 您使用的是哪个版本的 SQL Server?
  • Mike 感谢您的回复 - 我正在使用 sql server 2005
  • 如果输入/输出与一天边界重叠,您希望发生什么?显然,外出时间不会有相同的日期(因此您的结果签入/签出列可能需要日期部分)。只有结账时间的情况呢?只有签到时间?何时出现重复结帐?

标签: sql sql-server tsql date sql-server-2005


【解决方案1】:

试试这个 -

DECLARE @temp TABLE
(
    UserID INT,
    Checktime DATETIME,
    CheckStatus CHAR(1)
)

INSERT INTO @temp (UserID, Checktime, CheckStatus)
VALUES 
    (3175, '20131222 07:02:10.000', 'I'),
    (3175, '20131222 13:01:01.000', 'O'),
    (3175, '20131222 13:49:54.000', 'I'),
    (3175, '20131222 13:49:55.000', 'I'),
    (3175, '20131222 15:58:42.000', 'O'),
    (3175, '20131223 06:02:58.000', 'I'),
    (3175, '20131223 14:00:29.000', 'O'),
    (3175, '20131224 05:17:09.000', 'I'),
    (3175, '20131224 12:34:25.000', 'O'),
    (3175, '20131224 12:34:26.000', 'O')

SELECT 
      t.UserID
    , [Date] = DATEADD(dd, 0, DATEDIFF(dd, 0, t.CheckIn))
    , CheckIn = CONVERT(VARCHAR(10), t.CheckIn, 108)
    , CheckOut = CONVERT(VARCHAR(10), t.CheckOut, 108)
    , [Hours] = CAST(DATEDIFF(MINUTE, t.CheckIn, t.CheckOut) / 60. AS DECIMAL(10,2))
FROM (
    SELECT 
          t.UserID
        , CheckIn = t.Checktime
        , CheckOut = r.Checktime
        , RowNum = ROW_NUMBER() OVER (PARTITION BY t.UserID, r.Checktime ORDER BY 1/0)
    FROM @temp t
    OUTER APPLY (
        SELECT TOP 1 *
        FROM @temp t2
        WHERE t2.UserID = t.UserID
            AND t2.Checktime > t.Checktime
            AND DATEADD(dd, 0, DATEDIFF(dd, 0, t.Checktime)) = DATEADD(dd, 0, DATEDIFF(dd, 0, t2.Checktime))
            AND t2.CheckStatus = 'O'
        ORDER BY t2.Checktime
    ) r
    WHERE t.CheckStatus = 'I'
) t
WHERE t.RowNum = 1

输出 -

UserID      Date                    CheckIn    CheckOut   Hours
----------- ----------------------- ---------- ---------- --------
3175        2013-12-22 00:00:00.000 07:02:10   13:01:01   5.98
3175        2013-12-22 00:00:00.000 13:49:54   15:58:42   2.15
3175        2013-12-23 00:00:00.000 06:02:58   14:00:29   7.97
3175        2013-12-24 00:00:00.000 05:17:09   12:34:25   7.28

【讨论】:

  • 我们需要区分记录吗?你能解释一下上面的代码吗?
  • CROSS APPLY 子查询是否需要在这里排序?它可能无法保证在表格中的顺序正确。
  • Devart>感谢您的快速回复。该代码有效,但当员工签入并忘记签出时会产生不切实际的结果。代码所做的是取第一个 IN 和下一个 OUT,忽略第二天是否有另一个 IN。
  • 我可以确认结果确实准确并且是我想要的。我对查询的挑战是它非常慢。
  • 我猜这不需要实时运行?填充另一个报告表,然后在一夜之间批量填充。只是我的两分钱。查询很好,但对于那个大小的数据集,我认为你很难足够快地得到它。
【解决方案2】:

我采用了 Devart 的代码并对其进行了改进。我所做的是使用 OUTER APPLY 来获取每个“IN”状态的下一行。然后我整理了 where 子句中的坏行。如果连续有两个“IN”,则抓取后一个。

DECLARE @temp TABLE
(
    UserID INT,
    Checktime DATETIME,
    CheckStatus CHAR(1)
)

INSERT INTO @temp (UserID, Checktime, CheckStatus)
VALUES 
    (3175, '20131222 07:02:10.000', 'I'),
    (3175, '20131222 13:01:01.000', 'O'),
    (3175, '20131222 13:49:54.000', 'I'),
    (3175, '20131222 13:49:55.000', 'I'),
    (3175, '20131222 15:58:42.000', 'O'),
    (3175, '20131223 06:02:58.000', 'I'),
    (3175, '20131223 14:00:29.000', 'O'),
    (3175, '20131224 05:17:09.000', 'I'),
    (3175, '20131224 12:34:25.000', 'O'),
    (3175, '20131224 12:34:26.000', 'O')

SELECT  UserID,
        CAST(I.CheckTime AS DATE) AS [Date],
        CONVERT(VARCHAR(10), I.CheckTime, 108) AS CheckIn,
        CONVERT(VARCHAR(10), O.CheckTime, 108) AS CheckOut,
        CAST(DATEDIFF(MINUTE,I.checkTime,O.CheckTime)/60.0 AS DECIMAL(18,2)) [Hours]
FROM @temp I
OUTER APPLY (
                SELECT TOP 1    Checktime,
                                CheckStatus
                FROM @temp t
                WHERE       t.UserID = I.UserID
                        AND t.Checktime > I.Checktime
                ORDER BY t.Checktime
            ) O
WHERE I.CheckStatus = 'I'
AND O.CheckStatus = 'O'

结果:

UserID      Date       CheckIn    CheckOut   Hours
----------- ---------- ---------- ---------- -----
3175        2013-12-22 07:02:10   13:01:01   5.98
3175        2013-12-22 13:49:55   15:58:42   2.15
3175        2013-12-23 06:02:58   14:00:29   7.97
3175        2013-12-24 05:17:09   12:34:25   7.28

【讨论】:

    【解决方案3】:

    这个插入可以用 SELECT Query 改变吗?

    INSERT INTO @temp (UserID, Checktime, CheckStatus)
    VALUES 
        (3175, '20131222 07:02:10.000', 'I'),
        (3175, '20131222 13:01:01.000', 'O'),
        (3175, '20131222 13:49:54.000', 'I'),
        (3175, '20131222 13:49:55.000', 'I'),
        (3175, '20131222 15:58:42.000', 'O'),
        (3175, '20131223 06:02:58.000', 'I'),
        (3175, '20131223 14:00:29.000', 'O'),
        (3175, '20131224 05:17:09.000', 'I'),
        (3175, '20131224 12:34:25.000', 'O'),
        (3175, '20131224 12:34:26.000', 'O')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-01
      • 1970-01-01
      相关资源
      最近更新 更多