SQL:同一天的时间戳记间隔之和

时间:2019-06-18 15:21:30

标签: sql sql-server

我正在建立一个新的SQL查询,以汇总员工出勤表中的记录。这些记录是从指纹或RFID传感器下载的,并记录在同一张表上。我想得到工作时间。

如果员工每天进出工作场所一次,一切都很好。设备在表上生成2条记录,这不是问题。轻松确定出入时间。

但是我不知道如何解决此人是否进来,休息一下(离开工作场所),然后他再次进来直到退出时间。

假定它们在每个时间间隔上始终都是偶数记录(到达和退出时间戳记)。另外,员工从不检查一天,而在第二天离开。

我有以下查询。请记住:这只会获得最小时间戳(到达时间)和最大时间戳(离开时间)。

SELECT Userid, Name, Date, Entrance, Exit, Hours FROM
            (SELECT Userid AS user,
            CONVERT(VARCHAR, CONVERT(TIME, min(Checktime))) AS Entrance,
            CONVERT(VARCHAR, CONVERT(TIME, max(Checktime))) AS Exit,
            CONVERT(VARCHAR, CONVERT(TIME, max(Checktime)-min(CheckTime))) AS Hours,
            CONVERT(VARCHAR, CONVERT(DATE, CheckTime)) AS Fecha,
            COUNT(*) AS Regs,
            SUM(edited) AS edited FROM attendance
            WHERE CONVERT(DATE, CheckTime) < CONVERT(DATE, GETDATE())
            GROUP BY Userid, CONVERT(DATE, CheckTime)) AS Hs
            INNER JOIN Userinfo
            ON Userinfo.Userid = Hs.user
            ORDER BY Date DESC, Name ASC;

例如,如果表具有以下记录:

id  |  Logid   |  Userid  |       CheckTime       |  edited
1   |      10  |       1  |    2019-06-18 8:00:00 |     0
2   |      11  |       1  |   2019-06-18 12:00:00 |     0
3   |      12  |       1  |   2019-06-18 15:00:00 |     0
4   |      13  |       1  |   2019-06-18 17:00:00 |     0
5   |      14  |       2  |    2019-06-18 8:00:00 |     0
6   |      15  |       2  |   2019-06-18 17:00:00 |     0

我得到的是

Userid  |  Name     |     Date     |  Entrance  |   Exit   |  Hours  |  edited
     1  |  Gandalf  |  2019-06-18  |    8:00:00 | 17:00:00 | 9:00:00 |      0
     2  |    Frodo  |  2019-06-18  |    8:00:00 | 17:00:00 | 9:00:00 |      0

我需要什么:

Userid  |  Name     |     Date     |  Entrance  |   Exit   |  Hours  |  edited
     1  |  Gandalf  |  2019-06-18  |    8:00:00 | 17:00:00 | 6:00:00 |      0
     2  |    Frodo  |  2019-06-18  |    8:00:00 | 17:00:00 | 9:00:00 |      0

总时间由(12:00:00-8:00:00)+(17:00:00-15:00:00)计算得出。 在这种情况下,“入口”和“退出”列根本没有必要。

您知道我该如何解决吗?非常感谢你!

2 个答案:

答案 0 :(得分:2)

这假设您有一对输入/退出并处理多个中断。

SQL DEMO

with cte as (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY [Userid], cast ([CheckTime] as Date) 
                               ORDER BY [CheckTime]) as rn
  FROM Table1 t1
)  
SELECT c1.[Userid], 
       cast (c1.[CheckTime] as Date) as the_day,
       SUM (DATEDIFF (hh, c1.[CheckTime], c2.[CheckTime])) as total_hours
FROM cte c1
JOIN cte c2
  ON c1.rn = c2.rn -1
 AND c1.[Userid] = c2.[Userid]
 AND c1.rn % 2 = 1
GROUP BY c1.[Userid],
         cast (c1.[CheckTime] as Date) ;

输出

| Userid |    the_day | total_hours |
|--------|------------|-------------|
|      1 | 2019-06-18 |           6 |
|      2 | 2019-06-18 |           9 |

注意:

DATEDIFF的常规语法:

DATEDIFF(datepart, start_date, end_date)

只需实现函数DATEDIFF即可计算两个日期值之间的时间间隔,并将其作为整数返回。

因此,如果您使用hh作为日期部分的08:00和09:30,您仍然会获得1h。也许最好使用mi除以60

答案 1 :(得分:0)

完美! Juan Carlos的解决方案效果很好!

我之所以发布此帖子,是因为我已经编辑了他的一些代码以符合最初的发布要求。

代码完全相同。只有我更改/添加了几行

package example;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AccountTester implements Runnable {
    private Account account;
    private double amount;

    public AccountTester(Account account, double amount) {
        this.account = account;
        this.amount = amount;
    }

    public static void main(String[] args) {
        Account account = new Account(0);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.execute(new AccountTester(account, 1.0));
        executorService.execute(new AccountTester(account, 2.0));
        executorService.shutdown();
        System.out.println("End balance: " + account.getBalance());
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            account.deposit(amount);
        }
    }
}

此查询返回:

with cte as (
  SELECT  *, ROW_NUMBER() OVER (PARTITION BY [Userid], cast ([CheckTime] as Date)
                               ORDER BY [CheckTime]) as rn
  FROM Table1 t1
  WHERE CAST(CheckTime AS DATE) = '2019-06-17'  -- Filter by specific date 
)
SELECT c1.[Userid],
       cast (c1.[CheckTime] as Date) as the_day,
       -- Return time as HH:MM
       CONVERT(VARCHAR, SUM (DATEDIFF (SECOND , c1.[CheckTime], c2.[CheckTime]))/3600) + ':' + right('00' + CONVERT(VARCHAR, CONVERT(FLOAT, (SUM (DATEDIFF (SECOND , c1.[CheckTime], c2.[CheckTime]))/60) - ((SUM (DATEDIFF (SECOND , c1.[CheckTime], c2.[CheckTime]))/3600)*60))),2) as total_time
FROM cte c1
JOIN cte c2
  ON c1.rn = c2.rn -1
 AND c1.[Userid] = c2.[Userid]
 AND c1.rn % 2 = 1
GROUP BY c1.[Userid],
         cast (c1.[CheckTime] as Date);
相关问题