显示水平列出的状态之间的天数

时间:2018-04-08 11:12:48

标签: sql sql-server database

我有下表,它代表了票务数据库的操作细节。我想要做的是在每个票证上水平显示最后10个不同的操作,每个状态与前一个状态之间的日期差异(以天为单位)。

这是原始表:

Case_number Operation_Name  Date
112345      Canceled        22.04.2018
112345      On hold         20.04.2018
112345      On hold         15.04.2018
112345      Processing      10.04.2018
112345      Open            08.04.2018
112347      Closed          21.04.2018
112347      On hold         20.04.2018
112347      On hold         18.04.2018
112347      Processing      15.04.2018
112347      Processing      13.04.2018
112347      Open            11.04.2018

输出应该如下所示

enter image description here

如你所见:

112347      On hold         20.04.2018
112347      On hold         18.04.2018

可能存在重复状态,我必须仅提取第一个日期(18.04.2018)。

我尝试使用主导功能,但我没有运气。

提前感谢您的时间和想法!

2 个答案:

答案 0 :(得分:1)

可以将此作为一个直接的SQL查询,但需要一些肘部油脂。

基本上,我们需要 1.为每个case_number和。建立行顺序 2.执行双透视操作以逐步将Operation_Name列和Days_Elapsed列分别放入每列中

请参阅下文,了解最多可处理4个步骤的解决方案。如果需要,可以通过修改此步骤来添加更多步骤,这很简单。使用CTE和中间结果来了解制定此步骤的步骤。

SQL Fiddle

MS SQL Server 2017架构设置

create table test (id int, status varchar(max), date datetime)

insert test values 
(1, 'cancelled', '2018-04-07'),
(1, 'hold', '2018-04-05'),
(1, 'processing', '2018-04-04'),
(1, 'processing', '2018-04-02'),
(2, 'processing', '2018-04-01'),
(2, 'cancelled', '2018-04-07')

查询1

;WITH Ordered AS (
  SELECT id, status, date,
    ROW_NUMBER() OVER(PARTITION BY id ORDER BY date desc) AS rowNumber,
    LEAD(date) OVER(PARTITION BY id ORDER BY date desc) AS prevDate
  FROM test
) 
SELECT 
    id, 
    MAX([1]) AS Operation_Latest,
    MAX([D1]) AS DaysElapsed_Prev1_Latest,
    MAX([2]) AS Operation_Prev1, 
    MAX([D2]) AS DaysElapsed_Prev2_Prev1,
    MAX([3]) AS Operation_Prev2, 
    MAX([D3]) AS DaysElapsed_Prev3_Prev2,
    MAX([4]) AS Operation_Prev3,
    MAX([D4]) AS DaysElapsed_Prev4_Prev3
 FROM 
 (SELECT id, rowNumber, status, 
      DATEDIFF(day, prevDate, date) AS daysSinceLast,
      'D' + CONVERT(varchar, rowNumber) AS DaysPivot
  FROM Ordered) AS p
 PIVOT
 (
   MAX(status) FOR rowNumber IN ([1], [2], [3], [4])
 ) AS pv1
 PIVOT
 (
   MAX(daysSinceLast) FOR daysPivot IN ([D1], [D2], [D3], [D4])
 ) AS pv2
GROUP BY id

<强> Results

| id | Operation_Latest | DaysElapsed_Prev1_Latest | Operation_Prev1 | DaysElapsed_Prev2_Prev1 | Operation_Prev2 | DaysElapsed_Prev3_Prev2 | Operation_Prev3 | DaysElapsed_Prev4_Prev3 |
|----|------------------|--------------------------|-----------------|-------------------------|-----------------|-------------------------|-----------------|-------------------------|
|  1 |        cancelled |                        2 |            hold |                       1 |      processing |                       2 |      processing |                  (null) |
|  2 |        cancelled |                        6 |      processing |                  (null) |          (null) |                  (null) |          (null) |                  (null) |

答案 1 :(得分:1)

这将删除具有相同状态的其他行并分配序列号:

with cte as 
 (
   select *,
      lead(Operation_Name) -- previous Operation_Name
      over (partition by case_number
            order by Dat desc) as prev_op
   from tab
 )
select cte.*, 
   datediff(day
           ,date
           ,lag(date) -- next date
            over (partition by case_number
                  order by Date desc)
           ) as days_between,
   row_number()
   over (partition by case_number
         order by Date desc) as rn
from cte
where prev_op <> Operation_Name -- different value
   or prev_op is null           -- or first row
;

现在您可以将此传递给PIVOT或执行old-syle max(case):

with cte as 
 (
   select *,
      lead(Operation_Name) -- previous Operation_Name
      over (partition by case_number
            order by Date desc) as prev_op
   from tab
 )
, cte2 as
 (
   select cte.*, 
      datediff(day
              ,date
              ,lag(date) -- previous Operation_Name
               over (partition by case_number
                     order by Date desc)
) as days_between,
      row_number()
      over (partition by case_number
            order by Date desc) as rn
   from cte
   where prev_op <> Operation_Name -- different value
      or prev_op is null           -- or first row
 )
select case_number,
   max(case when rn = 1 then Operation_Name end),
   max(case when rn = 2 then Operation_Name end),
   max(case when rn = 2 then days_between end),
   max(case when rn = 3 then Operation_Name end),
   max(case when rn = 3 then days_between end),
   max(case when rn = 4 then Operation_Name end),
   max(case when rn = 4 then days_between end)
from cte2
group by case_number