查找多个刷入/刷出时间戳之间的总分钟数 - 从垂直行中选择多个记录并转换水平行

Posted

技术标签:

【中文标题】查找多个刷入/刷出时间戳之间的总分钟数 - 从垂直行中选择多个记录并转换水平行【英文标题】:Find Total Mins Between Multiple Swipe In/Swipe Out Timestamps - Select multiple records from vertical row and transform horizontal row 【发布时间】:2021-09-28 08:18:39 【问题描述】:

我正在处理 SQL Server 查询以获取将处理多个任务代码的用户列表的 Badge Out Time - Bade In Time。

task_mgmt - 表名

表格列如下

Task_Sn UserName Task_Code Action Badge_IN_OUT_TIME
1 Andy BLOG START 2021-07-20 08:11:45.000
2 Andy BLOG END 2021-07-20 10:11:45.000
3 Becky ACCTS START 2021-07-20 11:11:45.000
4 Becky ACCTS END 2021-07-20 12:11:45.000
5 Andy BLOG START 2021-07-20 12:15:45.000
6 Andy BLOG END 2021-07-20 12:25:45.000
7 Andy BLOG START 2021-07-20 12:25:00.000
8 Becky ACCTS START 2021-07-20 13:00:00.000
9 Becky ACCTS END 2021-07-20 13:30:00.000

我正在排除结果

UserName Task_Code Badge_IN_TIME Badge_OUT_TIME TOTAL_MINS_SPENT
Andy BLOG 2021-07-20 12:25:55.000 135
Becky ACCTS 2021-07-20 13:00:00.000 2021-07-20 13:30:00.000 90

我真的想不出任何关于这个的问题。我是 SQL 的初学者,我知道 CRUD 操作。这对我来说似乎超级复杂。

Select UserName,
       Task_Code,
       Badge_In_Time,
       Badge_Out_Time,
       (Badge_Out_Time - Badge_In_Time) as TOTAL_TIME_SPENT
from task_mgmt
order by Badge_IN_OUT_TIME desc

由于安迪目前是进入徽章但尚未退出徽章,因此最新的徽章退出时间戳被计算为 [ getdate() - badge-in ] 作为 LastTransaction Mins (5 mins )

【问题讨论】:

“我正在处理 SQL Server 查询” 那么请删除 'oracle' 标签 .... @EdStevens 我已经从标签列表中删除了 oracle。提前感谢您的帮助! 这能回答你的问题吗? Select records from vertical row and transform horizontal row 您之前的问题, 它与此有何不同,您从这些答案中遇到了什么问题?不要只是转发 @EdStevens 你有机会帮我写剧本吗? @JSLover 你能看看我的回答并澄清这些观点吗?在您的问题中或作为评论添加 Badge_IN_TIME Badge_OUT_TIME 列的派生详细信息。所以我也可以尝试获取这些列值。您已经很好地解释了 TOTAL_MINS_SPENT 的计算。对 Badge_IN_TIME Badge_OUT_TIME 执行相同的操作,因为我不确定您需要在那里显示什么。谢谢 【参考方案1】:

在您的示例数据中,Andy 的最后开始时间早于他的最后结束时间,因此产生了重叠。鉴于您如何描述计算总分钟数的逻辑,我假设这是不正确的。因此在我的示例表中我进行了相应的调整。

考虑以下几点:

我使用带有 LEAD() 函数的派生表来捕获下一个日期和操作的位置。我在我的外部查询中使用它来根据我的下一步操作来确定使用的分钟数。

我使用 CROSS APPLY 来确定用户名和任务的 MAX 开始日期和结束日期。

CREATE TABLE #tmp(Task_Sn int,  UserName varchar(20),Task_Code varchar(10), Action varchar(10), Badge_IN_OUT_TIME datetime)
INSERT INTO #tmp
VALUES
(1,'Andy','BLOG','START','2021-07-20 08:11:45.000'),
(2,'Andy','BLOG','END','2021-07-20 10:11:45.000'),
(3,'Becky','ACCTS','START','2021-07-20 11:11:45.000'),
(4,'Becky','ACCTS','END','2021-07-20 12:11:45.000'),
(5,'Andy','BLOG','START','2021-07-20 12:15:45.000'),
(6,'Andy','BLOG','END','2021-07-20 12:25:45.000'),
(7,'Andy','BLOG','START','2021-07-20 12:35:00.000'),
(8,'Becky','ACCTS','START','2021-07-20 13:00:00.000'),
(9,'Becky','ACCTS','END','2021-07-20 13:30:00.000')


SELECT X.UserName,X.Task_Code ,Y.ST_TIME Badge_IN_TIME
    ,CASE WHEN Y.ST_TIME > Y.ED_TIME THEN NULL ELSE Y.ED_TIME END Badge_OUT_TIME 
    ,SUM(CASE WHEN Action = 'START' AND NX_ACTION= 'END' THEN DATEDIFF(minute,Badge_IN_OUT_TIME, NX_TIME) 
         WHEN ACTION = 'START' AND NX_ACTION IS NULL THEN DATEDIFF(minute,Badge_IN_OUT_TIME, GETDATE())
         END) TotalMinutesSpent
FROM(
    select *,
    LEAD(Badge_IN_OUT_TIME,1) OVER(Partition by UserName,Task_Code  ORDER BY  Badge_IN_OUT_TIME) NX_TIME,
    LEAD(ACTION,1) OVER(Partition by UserName,Task_Code ORDER BY  Badge_IN_OUT_TIME) NX_ACTION
    from #tmp
    ) X
CROSS APPLY(SELECT UserName,
                MAX(CASE WHEN Action = 'START' THEN Badge_IN_OUT_TIME END) ST_TIME,
                MAX(CASE WHEN Action = 'END' THEN Badge_IN_OUT_TIME END) ED_TIME 
                FROM #tmp t1 
                    WHERE t1.UserName = X.UserName 
                            and t1.Task_Code = X.Task_Code 
                GROUP BY UserName, Task_Code
            ) Y
GROUP BY X.USERNAME,X.Task_Code ,Y.ST_TIME,Y.ED_TIME

【讨论】:

非常感谢。它就像魅力:)【参考方案2】:

这似乎有效:

SELECT UserName,
       Task_Code,
       task_in.Task_Sn as Task_In_Sn,
       TOUT.Task_Out_Sn,
       task_in.Badge_IN_OUT_TIME as Time_In_Raw,
       FORMAT(task_in.Badge_IN_OUT_TIME, 'hh\:mm') as Task_In_Time,
       FORMAT(TOUT.Task_Out_Time, 'hh\:mm') as Task_Out_Time,
       DATEDIFF(minute, task_in.Badge_IN_OUT_TIME,
                 ISNULL(TOUT.Task_Out_Time, GETDATE())) as LengthOfTime
FROM task_mgmt task_in
    OUTER APPLY (
       SELECT TOP 1
            task_out.Task_Sn as Task_Out_Sn,
            task_out.Badge_IN_OUT_TIME as Task_Out_Time
        FROM task_mgmt task_out
        WHERE task_out.Action = 'END' AND
              task_out.UserName = task_in.UserName AND
              task_out.Task_Sn > task_in.Task_Sn
        ORDER BY Task_Sn
    ) TOUT
WHERE task_in.Action = 'START'
ORDER BY Task_Sn

请注意,OUTER APPLY 查找与其他条件匹配的较大 Task_Sn 的第一条记录(我本可以使用 Badge_IN_OUT_TIME 而不是 Task_Sn,但整数更好一些,可能更有效) .

还要注意DATEDIFF 中的ISNULL,当日期为NULL 时,它会替换GETDATE

如果需要对多个区间求和,可以使用GROUP BY

【讨论】:

【参考方案3】:

我提供了答案,但我知道预期结果中缺少两列。

注意:这 两列 Badge_IN_TIME Badge_OUT_TIME 缺失,因为我不确定您要为这两列检索的内容,如果您可以澄清结果并这些两列的预期值与您为 TOTAL_TIME_SPENT 显示的方式一样,我可以编辑问题并添加这两列推导。

将逻辑添加到上述列之后,这就是我的查询返回的内容。我认为预期的答案也有一些问题,应该符合逻辑。

SELECT 
  DISTINCT 
  UserName
  , Task_Code
  ,( SELECT  MAX(Badge_IN_OUT_Time ) from  task_mgmt E where E.UserName =  A.UserName   and A.Task_code =  E.Task_Code and    [Action] ='Start'  )  Badge_IN_TIME
   ,( SELECT  MIN(Badge_IN_OUT_Time ) from  task_mgmt E where E.UserName =  A.UserName   and A.Task_code =  E.Task_Code and    [Action] ='END'  )  Badge_OUT_TIME
  , SUM( [DATEDIFF] ) OVER (PARTITION BY  UserName, Task_Code, [Action]  order by  UserName ) as  Total_MinsSpend
 FROM 
  (
   SELECT 
   Task_sn
 , UserName
 , Task_Code
 , [Action]
 , Badge_IN_OUT_Time 
 , ISNULL( LEAD(Badge_IN_OUT_Time) over  (Partition by UserName, Task_Code order by UserName ), '2021-07-20 12:30:00.000') as [StartTime_Lead]
 ,  DATEDIFF ( MINUTE,Badge_IN_OUT_Time ,iSNULL( LEAD(Badge_IN_OUT_Time) over  (Partition by UserName, Task_Code order by UserName ), '2021-07-20 12:30:00.000')  ) as [DATEDIFF] --Get the date dfference  
  FROM  task_mgmt
  )
 AS A Where A.[Action]  in ('Start')

【讨论】:

JSLover 你能否看看我的回答并澄清这些观点。在您的问题中或作为评论添加 Badge_IN_TIME Badge_OUT_TIME 列的派生详细信息。所以我也可以尝试获取这些列值。您已经很好地解释了 TOTAL_MINS_SPENT 的计算。对 Badge_IN_TIME Badge_OUT_TIME 执行相同的操作,我不确定您需要在那里显示什么。谢谢 在我们当前的表中,我们只有一列badge-in 和badge-out,我们通过引用Action (START, END) 来实现差异。 如果 Action 值为 "START" + Badge_IN_OUT_TIME = Badge_In_Time。如果 Action 值为 "END" + Badge_IN_OUT_TIME = Badge_Out_Time。 但在输出中,我们想显示最新的 Badge_In 和 Badge_Out 时间。 @JSLover 我认为 Total_Mins_spend 是正确的,因为我添加了 :)

以上是关于查找多个刷入/刷出时间戳之间的总分钟数 - 从垂直行中选择多个记录并转换水平行的主要内容,如果未能解决你的问题,请参考以下文章

Hivesql计算两个时间戳相差的分钟数

在php中查找2个unix时间戳之间的天数

使用 Pandas 的两个时间戳之间的每小时时间序列(以分钟为单位)

如何在java中获得小时/分钟差异

Java:1392515067621这种从1970年开始的毫秒数的时间戳叫啥时间戳呢?

SparkSQL - 两个时间戳之间的差异(以分钟为单位)