SQL Server:每个项目在每个州中的天数

时间:2014-11-26 14:30:04

标签: sql tsql sql-server-2012 cursor

给出一个存储每个项目的每个修订版的表。

例如:

+--------+----------+---------------+--------+---------------------+
| ItemId | Revision | PreviousState | State  |     DateChanged     |
+--------+----------+---------------+--------+---------------------+
|      1 |        1 | NULL          | New    | 2014-11-13 10:00:00 |
|      1 |        2 | New           | Active | 2014-11-15 10:00:00 |
|      1 |        3 | Active        | New    | 2014-11-17 10:00:00 |
|      1 |        4 | New           | Active | 2014-11-19 10:00:00 |
|      1 |        5 | New           | Active | 2014-11-20 10:00:00 |
|      1 |        6 | Active        | Closed | 2014-11-22 10:00:00 |
|      2 |        1 | NULL          | New    | 2014-11-13 10:00:00 |
|      2 |        2 | New           | Active | 2014-11-16 10:00:00 |
|      2 |        3 | Active        | Closed | 2014-11-17 10:00:00 |
|      2 |        4 | Closed        | Active | 2014-11-19 10:00:00 |
|      2 |        5 | Active        | Closed | 2014-11-21 10:00:00 |
+--------+----------+---------------+--------+---------------------+

我需要计算每个州每个项目的天数('关闭')。

结果应该是这样的:

+--------+-----+--------+
| ItemId | New | Active |
+--------+-----+--------+
|      1 |   4 |      5 |
|      2 |   3 |      3 |
+--------+-----+--------+

我尝试使用两种方法 - GROUP BY和嵌套游标。

使用游标(尤其是嵌套游标)是一种不好的做法。而且非常慢。

GROUP BY也没有工作,因为没有严格的州令(New - > Active - > Closed)。它可能是混乱的新 - >有效 - >已关闭 - >有效 - >已关闭 - >新 - >闭合。

在没有迭代所有记录和比较状态的情况下,我没有看到任何其他计算方法。

有没有解决方案?

提前致谢。

2 个答案:

答案 0 :(得分:1)

这为您提供了相同的结果,格式略有不同(但如果您需要完全相同的结果集,则可以轻松找到PIVOT个解决方案):

declare @t table (ItemId int,Revision int,State varchar(19),DateChanged datetime2)
insert into @t(ItemId,Revision,State,DateChanged) values
(1,1,'New',   '2014-11-13T10:00:00'),
(1,2,'Active','2014-11-15T10:00:00'),
(1,3,'New',   '2014-11-17T10:00:00'),
(1,4,'Active','2014-11-19T10:00:00'),
(1,5,'Active','2014-11-20T10:00:00'),
(1,6,'Closed','2014-11-22T10:00:00'),
(2,1,'New',   '2014-11-13T10:00:00'),
(2,2,'Active','2014-11-16T10:00:00'),
(2,3,'Closed','2014-11-17T10:00:00'),
(2,4,'Active','2014-11-19T10:00:00'),
(2,5,'Closed','2014-11-21T10:00:00')

;With Joined as (
    select t1.ItemId,t1.State,DATEDIFF(day,t1.DateChanged,t2.DateChanged) as Days
    from
        @t t1
            inner join
        @t t2
            on
                t1.ItemId = t2.ItemId and
                t1.Revision = t2.Revision -1
    )
select ItemId,State,SUM(Days)
from Joined
where State <> 'Closed'
group by ItemId,State

结果:

ItemId      State               
----------- ------------------- -----------
1           Active              5
1           New                 4
2           Active              3
2           New                 3

请注意,我忽略了您问题中的PreviousState列,而是构建Joined,因为当 下一个时真正重要的是强>国家生效。


由于您未在问题中描述这些问题而未处理的问题:1)如果当前最终状态不是Closed该怎么办 - 即我们是否忽略它,或者直到今天?,以及2)如果每个DateChanged的时间不相同怎么办 - 我们是否必须处理部分日子?

答案 1 :(得分:0)

我个人喜欢[Damien_The_Unbeliever]的CTE,我需要经常使用它们。使用内连接我基本上做同样的事情,在结果周围添加一个pivot包装器来获得你想要的东西:(替换@t作为你的真实表名)

SELECT ItemId , [New],[Active]
FROM
(
SELECT 
    ItemId , LASTSTATE,  DATEDIFF(D, LASTDATE, DateChanged) AS D 
FROM 
    @T AS T 
    INNER JOIN 
        (SELECT 
            ItemId as ItemLink, 
            Revision + 1 AS RevLink , 
            DateChanged AS LASTDATE , 
            State AS LASTSTATE from @t
        ) AS L ON T.ItemId = L.ItemLink AND T.Revision = L.RevLink
) AS P PIVOT ( SUM(D) FOR LASTSTATE IN ([New],[Active],[Closed])) AS DATA