2013-05-07 43 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