SQL Select 一个月的考勤表

SQL Select Statement for Time and attendance for a month

有人可以帮忙解决这个问题吗?我们的考勤系统生成以下数据:

Empid    Department  Timestamp                  Read_ID
3221      IT          2017-01-29 11:12:00.000    1
5565      IT          2017-01-29 12:28:06.000    1
5565      IT          2017-01-29 12:28:07.000    1
3221      IT          2017-01-29 13:12:00.000    2
5565      IT          2017-01-29 13:28:06.000    2
3221      IT          2017-01-30 07:42:15.000    1
3221      IT          2017-01-30 16:16:15.000    2
3221      IT          2017-01-31 09:05:00.000    1
3221      IT          2017-01-31 11:05:00.000    2
3221      IT          2017-01-31 13:20:00.000    1
3221      IT          2017-01-31 16:10:00.000    2

其中 Read_ID 值为:

我正在寻找 SQL 在 MS SQL 2014 上对 运行 的查询,该查询汇总了每个员工每月的出勤时间,例如

Empid   Department  Year    Month   TotalHours
3221    IT          2017    1       15:24
5565    IT          2017    1       01:00

试试这个。我不确定哪种时间格式可以满足您的系统要求,所以我将两者都放在了:

SELECT * INTO #Tbl3 FROM (VALUES
(3221,'IT','2017-01-29 11:12:00.000',1),
(5565,'IT','2017-01-29 12:28:06.000',1),
(5565,'IT','2017-01-29 12:28:07.000',1),
(3221,'IT','2017-01-29 13:12:00.000',2),
(5565,'IT','2017-01-29 13:28:06.000',2),
(3221,'IT','2017-01-30 07:42:15.000',1),
(3221,'IT','2017-01-30 16:16:15.000',2),
(3221,'IT','2017-01-31 09:05:00.000',1),
(3221,'IT','2017-01-31 11:05:00.000',2),
(3221,'IT','2017-01-31 13:20:00.000',1),
(3221,'IT','2017-01-31 16:10:00.000',2))
x (Empid,Department,Timestamp,Read_ID)

;With cte as (
    SELECT t1.Empid, t1.Department
        , [Year] = Year(t1.Timestamp)
        , [Month] = Month(t1.Timestamp)
        , Seconds = SUM(DATEDIFF(second, t1.Timestamp, t2.Timestamp))
    FROM #Tbl3 as t1
    OUTER APPLY (
        SELECT Timestamp = MIN(t.Timestamp) 
        FROM #Tbl3 as t
        WHERE t.Department = t1.Department and t.Empid = t1.Empid
            and t.Timestamp > t1.Timestamp and t.Read_ID = 2
    ) as t2
    WHERE t1.Read_ID = 1
    GROUP BY t1.Empid, t1.Department, Year(t1.Timestamp), Month(t1.Timestamp))
SELECT *, TotalHours = Seconds / 3600., TotalTime =
    RIGHT('0'+CAST(Seconds / 3600 as VARCHAR),2) + ':' +
    RIGHT('0'+CAST((Seconds % 3600) / 60 as VARCHAR),2) + ':' +
    RIGHT('0'+CAST(Seconds % 60 as VARCHAR),2)
FROM cte;

此查询应该会为您提供所需的结果。它的工作原理是选择每个条目,并将其与同一员工的下一个退出结合起来(没有进一步退出的条目将被忽略):这为我们提供了该员工轮班的持续时间。然后汇总结果并计算每组的班次持续时间。

SELECT
    t1.empid, 
    t1.department, 
    YEAR(t1.timestamp) Year,
    MONTH(t1.timestamp) Month,
    CONVERT(
        varchar(12), 
        DATEADD(minute, SUM(DATEDIFF(minute, t1.timestamp, t2.timestamp)), 0), 
        114
    ) TotalHours
FROM
    mytable t1
    INNER JOIN mytable t2 
        ON  t1.empid = t2.empid
        AND t2.read_id = 2
        AND t2.timestamp = (
            SELECT MIN(timestamp) 
            FROM mytable 
            WHERE 
                read_id = 2 
                AND empid = t2.empid 
                AND timestamp > t1.timestamp
        )
WHERE 
    t1.read_id = 1 
GROUP BY t1.empid, t1.department, YEAR(t1.timestamp), MONTH(t1.timestamp)
ORDER BY  1, 2, 3, 4

Returns :

 empid | department | Year | Month | TotalHours  
 ----: | :--------- | ---: | ----: | :-----------
  3221 | IT         | 2017 |     1 | 15:24:00:000
  5565 | IT         | 2017 |     1 | 02:00:00:000

DB Fiddle demo on SQL Server 2014


但是,存在一种极端情况,员工进入两次然后存在(这发生在您的数据中,员工 556529/01/2017 12:28:06 29/01/2017 12:28:07,然后在 29/01/2017 13:28:06 退出。上面的查询将考虑到两个重叠的条目并将它们映射到同一个出口,导致这一小时的工作被计算两次。

虽然这符合您的预期结果,但这是您真正想要的吗?这是一个替代查询,如果连续出现多个相同的员工条目,则只考虑最新的一个:

SELECT
    t1.empid, 
    t1.department, 
    YEAR(t1.timestamp) Year,
    MONTH(t1.timestamp) Month,
    CONVERT(
        varchar(12), 
        DATEADD(minute, SUM(DATEDIFF(minute, t1.timestamp, t2.timestamp)), 0), 
        114
    ) TotalHours
FROM
    mytable t1
    INNER JOIN mytable t2 
        ON  t1.empid = t2.empid
        AND t2.read_id = 2
        AND t2.timestamp = (
            SELECT MIN(timestamp) 
            FROM mytable 
            WHERE 
                read_id = 2 
                AND empid = t2.empid 
                AND timestamp > t1.timestamp
        )
WHERE 
    t1.read_id = 1 
    AND NOT EXISTS (
        SELECT 1 
        FROM mytable 
        WHERE 
            read_id = 1 
            AND empid = t1.empid 
            AND timestamp > t1.timestamp 
            AND timestamp < t2.timestamp
    ) 
GROUP BY t1.empid, t1.department, YEAR(t1.timestamp), MONTH(t1.timestamp)
ORDER BY  1, 2, 3, 4

Returns :

 empid | department | Year | Month | TotalHours  
 ----: | :--------- | ---: | ----: | :-----------
  3221 | IT         | 2017 |     1 | 15:24:00:000
  5565 | IT         | 2017 |     1 | 01:00:00:000

DB fiddle