查找不同间隔的票号的循环时间

Find the cycle time of a ticket number for different intervals

TicketNo    ActionDate                  OldStatus    NewStatus      CycleTime/Sec  
1001        2014-02-14 10:17:05.000     Assigned     InProgress     -
1001        2014-03-05 02:03:44.000     InProgress   Reply          1611999
1001        2014-03-11 10:00:14.000     Reply        Resolved       546990
1002        2015-03-20 04:44:14.000     InProgress   Reply          -
1002        2015-03-21 05:40:02.000     Reply        Resolved       89748

我必须计算工单状态的每次变化的周期时间。

在上面的示例中,我尝试计算工单从旧状态路由到新状态时从操作日期算起的秒数。

我尝试使用排名函数,但没有得到想要的输出。

select * ,row_number() 结束(按 a.ticketno 划分,a.oldstatus 按 a.actiondate 排序)rn

来自票务

如果有人能提出一些解决此计算的想法,我将不胜感激。

最简单的方法是使用 LAG。您可以在此处阅读有关 LAG 函数的更多信息。 https://docs.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql?view=sql-server-ver15

这是一个功能齐全的示例。

declare @Something table
(
    TicketNo int
    , ActionDate datetime
    , OldStatus varchar(50)
    , NewStatus varchar(50)
)

insert @Something values
(1001, '2014-02-14 10:17:05.000', 'Assigned', 'InProgress')
, (1001, '2014-03-05 02:03:44.000', 'InProgress', 'Reply')
, (1001, '2014-03-11 10:00:14.000', 'Reply', 'Resolved')
, (1002, '2015-03-20 04:44:14.000', 'InProgress', 'Reply')
, (1002, '2015-03-21 05:40:02.000', 'Reply', 'Resolved')

select s.*
    , CycleTimeSeconds = datediff(second, lag(ActionDate, 1) over(partition by TicketNo order by ActionDate), s.ActionDate)
from @Something s

--编辑--

这是一个可以与 Sql Server 2008 一起使用的版本(您确实应该考虑升级,因为不再支持该版本)。

select s.*
    , CycleTimeSeconds = datediff(second, MyLag.ActionDate, s.ActionDate)
from @Something s
outer apply 
(
    select top 1 ActionDate 
    from @Something s2 
    where s2.TicketNo = s.TicketNo 
        and s2.ActionDate < s.ActionDate 
    order by ActionDate desc
) MyLag

我采用了 Sean 的出色解决方案并对其进行了调整以避免排序。我正在使用一个临时变量 table,这样我就可以从我即将 post 的执行计划中排除临时变量 creation/population。

-- Temp table with sample data
IF OBJECT_ID('tempdb..#Something') IS NOT NULL DROP TABLE #Something;
CREATE TABLE #Something
(    TicketNo INT
    , ActionDate DATETIME
    , OldStatus  VARCHAR(50)
    , NewStatus  VARCHAR(50)
);
INSERT #Something VALUES
  (1001, '2014-02-14 10:17:05.000', 'Assigned', 'InProgress')
, (1001, '2014-03-05 02:03:44.000', 'InProgress', 'Reply')
, (1001, '2014-03-11 10:00:14.000', 'Reply', 'Resolved')
, (1002, '2015-03-20 04:44:14.000', 'InProgress', 'Reply')
, (1002, '2015-03-21 05:40:02.000', 'Reply', 'Resolved')

-- TOP (1) Solution
SELECT s.TicketNo, s.ActionDate, s.OldStatus, s.NewStatus,
       CycleTimeSeconds = DATEDIFF(SECOND, MyLag.ActionDate, s.ActionDate)
FROM   #Something AS s
OUTER APPLY
(
    SELECT TOP (1) ActionDate
    FROM     #Something s2 
    WHERE    s2.TicketNo   = s.TicketNo 
    AND      s2.ActionDate < s.ActionDate
    ORDER BY s2.ActionDate DESC
) AS MyLag;

-- Using MAX instead of TOP (1) to avoid a DESC sort operation
SELECT s.TicketNo, s.ActionDate, s.OldStatus, s.NewStatus,
       CycleTimeSeconds = DATEDIFF(SECOND, MyLag.ActionDate, s.ActionDate)
FROM #Something AS s
CROSS APPLY
(
    SELECT ActionDate = MAX(ActionDate)
    FROM   #Something s2 
    WHERE  s2.TicketNo   = s.TicketNo 
    AND    s2.ActionDate < s.ActionDate 
) AS MyLag;

由于子查询只评估一列,我们可以利用没有 GROUP BY 的聚合函数。因为我使用的是聚合 (MAX),所以我总是会返回一行,这就是我将 OUTER APPLY 更改为 CROSS APPLY 的原因。 OUTER APPLY 没有错,但更改它会从该执行计划中删除标量运算符 - 没有性能提升,只是一个更清晰的执行计划。

这种方法还有一个非常大的额外好处:它不仅避免了排序,我们还避免了 DESCending 排序。如果 ActionDate 上有索引,优化器可以利用它通过执行 *ordered-backward 扫描来避免降序排序。将 ActionDate 上的 UNIQUE 约束添加到原始临时变量(我不建议这样做,但它适用于此示例)运行 按 ActionDate DESC 排序的 TOP (1) 查询。

declare @Something table
(
    TicketNo int
    , ActionDate datetime UNIQUE
    , OldStatus varchar(50)
    , NewStatus varchar(50)
)

insert @Something values
  (1001, '2014-02-14 10:17:05.000', 'Assigned', 'InProgress')
, (1001, '2014-03-05 02:03:44.000', 'InProgress', 'Reply')
, (1001, '2014-03-11 10:00:14.000', 'Reply', 'Resolved')
, (1002, '2015-03-20 04:44:14.000', 'InProgress', 'Reply')
, (1002, '2015-03-21 05:40:02.000', 'Reply', 'Resolved')

SELECT TOP (1) ActionDate
FROM     @Something AS s2 
ORDER BY s2.ActionDate DESC;

注意向后扫描:

向后扫描杀死并行性。 Itzik Ben-Gan 在这里对此进行了讨论:Avoiding a Sort with Descending Order

One difference between ordered-forward and ordered-backward scans is that the former can potentially use parallelism, whereas the latter doesn’t currently have an implementation with parallelism in the storage engine

此处 APPLY 允许我们进行无排序操作 ,并在需要时在优化器的后袋中进行 并行处理。更多证据表明 APPLY 很棒 ... 不需要任何进一步的证据 ;)