2013-05-07 106 views
1

我有一个表,其中包含n历史记录条目,记录活动/非活动状态的变化。SQL优化计算总时间跨度的方法

[Id] [ParentId] [Date]     [Status] 
<guid> 0   2013-05-03 15:51:24.810 'Active' 
<guid> 0   2013-05-03 15:52:10.773 'Inactive' 
<guid> 0   2013-05-03 15:54:26.710 'Active' 
<guid> 0   2013-05-03 17:09:27.327 'Inactive' 

我试图确定项目在整个历史中处于“活动”状态的时间。用游标迭代历史记录并使用DATEDIFF来计算它的性能非常糟糕。我试图避免使用SQLCLR,但可能在内存中以相当便宜的方式执行此操作......有谁知道完成此操作的良好的SQL本机高性能方法吗?

+0

“自我加入”这个词让人想起。有某种记录ID吗? – 2013-05-07 19:45:22

+0

是的,对不起,历史表上有一个UUID列,父对象上有FK列。 - 用更好的示例表更新了问题。 – lukiffer 2013-05-07 19:47:14

+0

*'我试图确定该项目处于“活动”状态的时间长度*“ - 如何识别”项目“?它是'[Id]','[ParentId]'还是其他一些不包含的字段?另外,你可以修改现有的桌面设计吗? – 2013-05-07 19:52:23

回答

2

关键的想法是在给定的活动记录之后获取下一个非活动记录。

如果您使用的是SQL Server 2012,那么您可以使用lead()函数。否则,我认为相关的子查询是表示需要完成的最简单的方法。 (你也可以用明确的join来做到这一点,我只是认为这更清晰)。

select guid, sum(datediff(ms, t.[date], t.nextInactive)) as duration_ms 
from (select t.*, 
      (select min([date]) 
       from t t2 
       where t2.guid = t.guid and t2.[date] > t.[date] and t2.status = 'Inactive' 
      ) as nextInactive 
     from t 
     where t.status = 'Active' 
    ) t 
group by guid 

一旦你的下一个不活动的记录,那么就采取差异(在这种情况下,以毫秒为单位),并添加了值。

如果最后一条记录是活动记录,则它将被忽略。如果你想要计数,那么你需要在外部查询中使用coalesce(t.nextInactive, <some value here>)

如果您有guid, status, date索引,性能将得到提高。

+0

这很完美。要用'LEAD()'来玩,看看它是否有更多的表现,但子查询效果很好! – lukiffer 2013-05-07 20:02:14