【问题标题】:SQL Server : how many days each item was in each stateSQL Server:每个项目处于每个状态的天数
【发布时间】:2014-11-26 14:30:03
【问题描述】:

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

例如:

+--------+----------+---------------+--------+---------------------+
| 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 -> Active -> Closed -> New -> Closed。

如果不迭代所有记录并比较状态,我看不到任何其他计算方法。

有什么解决办法吗?

提前致谢。

【问题讨论】:

  • 对我来说似乎是一个存储过程或脚本(如 php)任务
  • fwiw,我会尽力避免 NULL。它使各种标准变得不必要地复杂,因为在设计上,NULL 仅适用于 IS NULL 和 IS NOT NULL。例如,您可以在 PreviousState 上使用 N/A 来获取 NEW。

标签: sql tsql sql-server-2012 cursor


【解决方案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 的时间不同怎么办 - 我们必须处理部分日期吗?

【讨论】:

  • CTE 比旧的子查询干净得多,我肯定需要更频繁地使用它们,只是因为它们更易于阅读和调试。 -- 干净整洁的代码
  • 1) 如果当前的最终状态不是 Closed 怎么办 - 即我们是忽略它,还是算到今天? -- 最终状态将始终关闭。我过滤了所有未关闭的项目。
  • 2) 如果每个 DateChanged 的​​时间不同怎么办 - 我们必须处理部分日期吗? - 是的。据我所知 DATEDIFF() 应该处理这种情况。谢谢。
  • @GhostMaster - DATEDIFF 总是返回整数 - 所以如果你问今天上午 10 点和晚上 10 点之间的差异,答案是 0。相反,今天晚上 10 点和明天上午 10 点之间的差异是 1。它测量转换 - 今晚 11.59pm 和明天 00.01am 之间的差异也是 1。如果您需要处理累积的部分天数,您可能希望以小时(或分钟或秒)为单位执行 DATEDIFFs,然后除以结果适当。
【解决方案2】:

我个人喜欢 [Damien_The_Unbeliever] 的 CTE,我需要更频繁地使用它们。使用内部连接我基本上做同样的事情,在结果周围添加一个枢轴包装器以获得您正在寻找的内容:(将 @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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-08
    • 2020-10-29
    • 1970-01-01
    • 1970-01-01
    • 2019-07-26
    • 2021-09-28
    • 1970-01-01
    • 2019-01-02
    相关资源
    最近更新 更多