【问题标题】:Calculate time difference between unique rows of data计算唯一数据行之间的时间差
【发布时间】:2018-03-23 20:24:40
【问题描述】:

假设我有一个包含两个字段的表。 Record_CreateddatetimeAction 只是一个字符串。数据可能如下所示:

Record_Created      Action
1/11/18 5:24 PM     Action 1
1/11/18 5:32 PM     Action 2
1/17/18 4:41 PM     Action 3
1/17/18 4:41 PM     Action 2
1/17/18 4:44 PM     Action 3
1/18/18 11:12 AM    Action 4
1/18/18 11:12 AM    Action 3
1/18/18 11:13 AM    Action 4
1/25/18 2:44 PM     Action 5

我需要计算不同操作之间的时间差(以天为单位),而不仅仅是单个行之间,而是基于每个操作的最后一次出现的唯一操作。所以我的结果数据集应该是这样的:

Action  Difference
Action 2    6
Action 3    1
Action 4    0
Action 5    7

考虑到我在此表中有超过一百万条记录要处理,实现这一目标的最佳和最有效的方法是什么?

【问题讨论】:

    标签: sql sql-server tsql datetime calculated-columns


    【解决方案1】:

    您可以获取每种操作类型的最小和最大日期,并使用datediff 来获取它们之间的天数:

    SELECT   action, DATEDIFF(DAY, MIN(record_created), MAX(record_created))
    FROM     mytable
    GROUP BY action
    HAVING   COUNT(*) > 1
    

    【讨论】:

    • 不幸的是,这并不容易。再次阅读请求。这是关于每个操作的最后日期以及这些日期之间的时间跨度。这就是为什么第 5 项行动与第 4 项行动相差 7 天的原因。
    【解决方案2】:

    如果我理解正确,您想查看每个操作的最后日期,然后列出按该日期排序的操作,并显示从一个操作到下一个操作的时间跨度(以天为单位)。

    因此通过操作聚合以获取最后日期,然后使用LAG 来查看之前的记录。

    select 
      action, 
      max(record_created),
      date_diff(day,
        lag(max(record_created)) over (order by max(record_created)),
        max(record_created)
      ) as diff;
    from actions
    group by action
    order by action;
    

    此查询还包含第一个操作(差异 = null),但我想您不介意。

    Rextester 演示:http://rextester.com/EAA26233

    【讨论】:

    • 嗨,Thorsten,是的,你说对了。这正是我所需要的。这仅适用于表中的记录子集。假设我在同一张表中有类似 ProductID 的内容,并且还想在 ProductID 中对操作进行分组,我只需要将 ProductID 添加到 GROUP BY 子句中,所以它看起来像.....GROUP BY PoductID, Action?
    • 是的,这听起来很正确。 select productid, action, ... group by productid, action.
    • 有趣的是,这段代码只适用于记录的子集,而不是整个数据集。例如,如果我将 productid 添加到临时表中,我只会得到为第一组 ProductID 计算的时间差。我刚刚将 ProductID 添加到您的临时表中,并插入了另外 2 组具有相同确切日期的产品:rextester.com/QCET45072
    • 做了一个小改动,它有点工作了 (rextester.com/JMUI80775) 但可以理解的是,每个子集中第一条记录的天差计算错误,因为它查看的是上一个子集的最后一条记录。现在我相当有信心第一条记录必须存在于每个子集中,所以我可以用 0 填充它,但我很好奇是否有一种聪明的方法可以只在同一个数据子集中进行此计算(在我的情况下,就在同一个 ProductID 中)?
    • 哦,对不起,我的错。当按productid, action分组时,必须添加partion子句,即将over (order by max(record_created))改为over(partition by productid order by max(record_created))
    【解决方案3】:

    我不知道你从哪里得到行动 5

    declare @T table (dt datetime, action varchar(10));
    insert into @T values 
           ('1/11/18 5:24 PM',  'Action 1')
         , ('1/11/18 5:32 PM',  'Action 2')
         , ('1/17/18 4:41 PM',  'Action 3')
         , ('1/17/18 4:41 PM',  'Action 2')
         , ('1/17/18 4:44 PM ', 'Action 3')
         , ('1/18/18 11:12 AM', 'Action 4')
         , ('1/18/18 11:12 AM', 'Action 3')
         , ('1/18/18 11:13 AM', 'Action 4')
         , ('1/25/18 2:44 PM',  'Action 5');
    
    select * from @t order by action, dt desc
    
    select tt.action, tt.dt, tt.leaddt, DATEDIFF(day, tt.leaddt, tt.dt) as diff 
      from ( select t.* 
                  , ROW_NUMBER() over (partition by t.action order by t.dt desc) as rn 
                  , lead(t.dt)   over (partition by t.action order by t.dt desc) as leaddt 
               from @T t 
           ) tt
    where tt.rn = 1 
      and tt.leaddt is not null 
    order by tt.action
    
    dt                      action
    ----------------------- ----------
    2018-01-11 17:24:00.000 Action 1
    2018-01-17 16:41:00.000 Action 2
    2018-01-11 17:32:00.000 Action 2
    2018-01-18 11:12:00.000 Action 3
    2018-01-17 16:44:00.000 Action 3
    2018-01-17 16:41:00.000 Action 3
    2018-01-18 11:13:00.000 Action 4
    2018-01-18 11:12:00.000 Action 4
    2018-01-25 14:44:00.000 Action 5
    
    action     dt                      leaddt                  diff
    ---------- ----------------------- ----------------------- -----------
    Action 2   2018-01-17 16:41:00.000 2018-01-11 17:32:00.000 6
    Action 3   2018-01-18 11:12:00.000 2018-01-17 16:44:00.000 1
    Action 4   2018-01-18 11:13:00.000 2018-01-18 11:12:00.000 0
    

    【讨论】:

    • 感谢您的建议,但这并不是我想要的结果。
    • 抱歉,如果我不清楚。您在结果集中遗漏了最新的 Action 4 和 Action 5 之间的区别。
    【解决方案4】:

    这可能是一种蛮力解决方案,但它应该可以完成工作。逻辑是 1.获取每个动作的最大日期 2. 为每条记录分配行号,以便您可以在子查询中迭代。 3.计算差异

    ;WITH cte1 as
    (select action, max(record_created) as MaxDt, ROW_Number() OVER(Order by Action) as row_num
     from @YourTable
     group by action
    ) 
    
    select *, (select DATEDIFF(DAY, b.MaxDT, a.MaxDT) 
                from cte1 b 
               where b.row_num= a.row_num-1 ) as Diff
    from cte1 a 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-04-23
      • 2019-11-11
      • 1970-01-01
      • 1970-01-01
      • 2019-01-11
      • 2020-10-25
      相关资源
      最近更新 更多