SQL将行转换为列以跟踪作业状态

时间:2017-09-21 18:10:55

标签: sql sql-server sql-server-2012

SQL Server 2012.我需要创建一个查询来确定哪些作业在过去的任何给定日期处于哪种状态(BONUS:他们处于该状态的时间长度。)

我有一个Job Status Log表,其中包含以下列和结构:

JobStatusNo JobNo   Status  Rem         Entered                 EnteredBy
-------------------------------------------------------------------------
1644897     420969  801     Reschedule  2017-09-20 17:58:18.503 1488
1644896     420969  812     Cancelled   2017-09-15 08:20:48.390 1267
1644895     420969  803     Confirmed   2017-09-14 10:13:25.733 1231
1644894     420969  802     Call Bob    2017-09-14 09:35:57.337 1231
1644893     420969  801                 2017-09-08 18:18:16.490 1488
1644892     420965  807                 2017-09-20 17:55:02.660 1488
1644891     420965  809                 2017-09-20 17:47:52.340 1488
1644890     420965  806                 2017-09-20 17:40:22.580 1488
1644889     420965  803     Confirmed   2017-09-20 17:05:30.870 1193
1644888     420965  801                 2017-09-20 17:05:29.130 1193
1644877     420964  801                 2017-09-20 17:02:16.830 1193

我想我希望有一个特定的工作号码,然后是每个状态,以及何时(另外我不关心评论或谁进入工作):

JobNo   1Status 1Entered                2Status 2Entered                
--------------------------------------------------------------
420969  801     2017-09-20 17:58:18.503 812     2017-09-15.337
420968  801     2017-09-20 17:55:02.660
420967  801     2017-09-20 17:47:52.340
420966  801     2017-09-20 17:40:22.580
420965  803     2017-09-20 17:05:30.870
420965  801     2017-09-20 17:05:29.130
420964  801     2017-09-20 17:02:16.830

...在指示3Status和3Entered之后有更多列。我只需要编码8个状态/输入日期,因为这是作业重新排序或替换状态的最多次数。如果最终有更多列,我将能够扩展我在这里的任何答案以包含该逻辑。

...因为我最终的答案"将于2016年7月1日(任何指定日期):87个职位处于801状态,255个职位处于806状态,5个职位处于809状态。事实上,我需要做数学来确定最终每个工作的特定状态如何,但这是我的第一个问题而且我不知道我的答案会有多复杂,我称之为在这里,我猜测一旦我通过DateDiff获得这些状态和日期,我就可以弄明白其余部分。

我已经尝试过从UNPIVOT,Lag / Lead,分组,MAX等等我能想到的所有组合,并且无处可去。

在这一点上,我甚至可能会错过一些简单的东西,并且会对答案感到非常愚蠢,但我很好并且真的被困住了。我是否正在尝试从他们目前所在的行中获取这些列?有没有办法采取给定的日期并按原样使用表?如果有什么不清楚,我会尝试澄清更新或答案。干杯!

以下是我从@Fercstar中选择的答案:

WITH A
AS
(
    SELECT
     *
    ,ROW_NUMBER() OVER(PARTITION BY JobNo ORDER BY Entered DESC) as StatusOrder
    FROM MyJobStatusTable
)

SELECT
 A.JobNo
,A.Status as Status1
,A.Entered as Entered1
,A2.Status as Status2
,A2.Entered as Entered2
,A3.Status as Status3
,A3.Entered as Entered3
,A4.Status as Status4
,A4.Entered as Entered4
,A5.Status as Status5
,A5.Entered as Entered5
,A6.Status as Status6
,A6.Entered as Entered6
,A7.Status as Status7
,A7.Entered as Entered7
,A8.Status as Status8
,A8.Entered as Entered8
,A9.Status as Status9
,A9.Entered as Entered9

FROM A
LEFT JOIN A as A2
    ON A2.JobNo = A.JobNo
    AND A2.StatusOrder = 2
LEFT JOIN A as A3
    ON A3.JobNo = A.JobNo
    AND A3.StatusOrder = 3
LEFT JOIN A as A4
    ON A4.JobNo = A.JobNo
    AND A4.StatusOrder = 4
LEFT JOIN A as A5
    ON A5.JobNo = A.JobNo
    AND A5.StatusOrder = 5
LEFT JOIN A as A6
    ON A6.JobNo = A.JobNo
    AND A6.StatusOrder = 6
LEFT JOIN A as A7
    ON A7.JobNo = A.JobNo
    AND A7.StatusOrder = 7
LEFT JOIN A as A8
    ON A8.JobNo = A.JobNo
    AND A8.StatusOrder = 8
LEFT JOIN A as A9
    ON A9.JobNo = A.JobNo
    AND A9.StatusOrder = 9

WHERE A.StatusOrder = 1

在超过一百万行数据的情况下在12秒内运行,无需临时表管理。优雅!谢谢@Fercstar。

3 个答案:

答案 0 :(得分:0)

如果您的状态数量有限,则可以使用

    DECLARE @table table (JobStatusNo int , JobNo   int, Status  int,  Rem  varchar(25) , Entered datetime, EnteredBy int ) 
Insert @table (JobStatusNo ,JobNo   ,Status  ,Rem         ,Entered                 ,EnteredBy) values 
 (1644897     ,420969  ,801     ,'Reschedule'  ,'2017-09-20 17:58:18.503', 1488)
,(1644896     ,420969  ,812     ,'Cancelled'   ,'2017-09-15 08:20:48.390', 1267)
,(1644895     ,420969  ,803     ,'Confirmed'   ,'2017-09-14 10:13:25.733', 1231)
,(1644894     ,420969  ,802     ,'Call Bob '   ,'2017-09-14 09:35:57.337', 1231)
,(1644893     ,420969  ,801     ,'         '   ,'2017-09-08 18:18:16.490', 1488)
,(1644892     ,420968  ,801     ,'         '   ,'2017-09-20 17:55:02.660', 1488)
,(1644891     ,420967  ,801     ,'         '   ,'2017-09-20 17:47:52.340', 1488)
,(1644890     ,420966  ,801     ,'         '   ,'2017-09-20 17:40:22.580', 1488)
,(1644880     ,420965  ,803     ,'Confirmed'   ,'2017-09-20 17:05:30.870', 1193)
,(1644879     ,420965  ,801     ,'         '   ,'2017-09-20 17:05:29.130', 1193)
,(1644877     ,420964  ,801     ,'         '   ,'2017-09-20 17:02:16.830', 1193)


;With CTE as (
SELECT T.JobNo 
,CASE WHEN Substring(CAST ( T.Status as char(3)),2,1) = '0'  THEN T.Status ELSE NULL END [1Status]  
,CASE WHEN Substring(CAST ( T.Status as char(3)),2,1) = '0'  THEN T.Entered ELSE NULL END [1Entered]
,CASE WHEN Substring(CAST ( C.Status as char(3)),2,1) = '1'  THEN C.Status ELSE NULL END [2Status]  
,CASE WHEN Substring(CAST ( C.Status as char(3)),2,1) = '1'  THEN C.Entered ELSE NULL END [2Entered]

from @table t Cross apply (Values(Status , Entered)) C (Status,Entered)
)
SELECT 
JobNo   
,MIN([1Status])  [1Status   ]
,MIN([1Entered]) [1Entered] 
,MAX([2Status ]) [2Status   ]
,MAX([2Entered]) [2Entered]
,Convert(char(8), dateadd(MINUTE, DATEDIFF(Minute,MIN([1Entered]),MAX([2Entered])), ''), 114) StatusTime
FROm Cte
Group By 
JobNo    

结果

JobNo       1Status     1Entered                2Status     2Entered                StatusTime
----------- ----------- ----------------------- ----------- ----------------------- ---------------
420964      801         2017-09-20 17:02:16.830 NULL        NULL                    NULL
420965      801         2017-09-20 17:05:29.130 NULL        NULL                    NULL
420966      801         2017-09-20 17:40:22.580 NULL        NULL                    NULL
420967      801         2017-09-20 17:47:52.340 NULL        NULL                    NULL
420968      801         2017-09-20 17:55:02.660 NULL        NULL                    NULL
420969      801         2017-09-08 18:18:16.490 812         2017-09-15 08:20:48.390 14:02:00

答案 1 :(得分:0)

您可以使用

ROW_NUMBER() OVER(PARTITION BY JobNo ORDER BY Entered DESC)

这将获得作业的状态顺序,其中1是最新的。 然后你可以按此转动。

编辑 - 基于样本数据的完整查询

With A
As
(
Select *, ROW_NUMBER() OVER(PARTITION BY JobNo ORDER BY Entered DESC) as StatusOrder From #Table
)

Select

A.JobNo

,A.Status as Status1

,A.Entered as Entered1

,A2.Status as Status2

,A2.Entered as Entered2

,A3.Status as Status3

,A3.Entered as Entered3

,A4.Status as Status4

,A4.Entered as Entered4

,A5.Status as Status5

,A5.Entered as Entered5

,A6.Status as Status6

,A6.Entered as Entered6

,A7.Status as Status7

,A7.Entered as Entered7

,A8.Status as Status8

,A8.Entered as Entered8

From A
Left Join A as A2
on A2.JobNo = A.JobNo and A2.StatusOrder = 2
Left Join A as A3
on A3.JobNo = A.JobNo and A3.StatusOrder = 3
Left Join A as A4
on A4.JobNo = A.JobNo and A4.StatusOrder = 4
Left Join A as A5
on A5.JobNo = A.JobNo and A5.StatusOrder = 5
Left Join A as A6
on A6.JobNo = A.JobNo and A6.StatusOrder = 6
Left Join A as A7
on A7.JobNo = A.JobNo and A7.StatusOrder = 7
Left Join A as A8
on A8.JobNo = A.JobNo and A8.StatusOrder = 8

Where A.StatusOrder = 1

答案 2 :(得分:0)

我确实知道这是不是你想要的。我创建了一个获取JobNo的代码,并根据时间显示它的所有状态。

CREATE TABLE ##JobStatusLog  (JobStatusNo int , JobNo   int, Status  int,  Rem  varchar(25) , Entered datetime, EnteredBy int ) 

CREATE TABLE ##tmpJobStatusLog (JobNo   int, Status  int,Entered datetime, Sequence int , StatusName varchar(20) , EnteredName varchar(20))

CREATE TABLE ##StatusSequence(Sequence int, StatusName varchar(20) , EnteredName varchar(20))

数据加载

Insert ##JobStatusLog (JobStatusNo ,JobNo   ,Status  ,Rem         ,Entered                 ,EnteredBy) values 
 (1644897     ,420969  ,801     ,'Reschedule'  ,'2017-09-20 17:58:18.503', 1488)
,(1644896     ,420969  ,812     ,'Cancelled'   ,'2017-09-15 08:20:48.390', 1267)
,(1644895     ,420969  ,803     ,'Confirmed'   ,'2017-09-14 10:13:25.733', 1231)
,(1644894     ,420969  ,802     ,'Call Bob '   ,'2017-09-14 09:35:57.337', 1231)
,(1644893     ,420969  ,801     ,'         '   ,'2017-09-08 18:18:16.490', 1488)
,(1644892     ,420968  ,801     ,'         '   ,'2017-09-20 17:55:02.660', 1488)
,(1644891     ,420967  ,801     ,'         '   ,'2017-09-20 17:47:52.340', 1488)
,(1644890     ,420966  ,801     ,'         '   ,'2017-09-20 17:40:22.580', 1488)
,(1644880     ,420965  ,803     ,'Confirmed'   ,'2017-09-20 17:05:30.870', 1193)
,(1644879     ,420965  ,801     ,'         '   ,'2017-09-20 17:05:29.130', 1193)
,(1644877     ,420964  ,801     ,'         '   ,'2017-09-20 17:02:16.830', 1193)

准备数据

;WITH CTE
AS 
(
SELECT 
* 
, Sequence = ROW_NUMBER() OVER (PARTITION BY JobNo ORDER BY  Entered)
FROM 
##JobStatusLog
)
INSERT ##tmpJobStatusLog 
SELECT JobNo   , Status  ,Entered , Sequence , 'Status' + CAST(Sequence as varchar(9))  StatusName , 'Entered' + CAST(Sequence as varchar(9)) EnteredName 
FROM CTE ORDER BY Status

准备列名

INSERT ##StatusSequence
SELECT DISTINCT Sequence ,StatusName, EnteredName FROM ##tmpJobStatusLog

declare @sql nvarchar(max) =''
declare @columnsStatus nvarchar(max) =''
declare @columnsEntered nvarchar(max) =''
declare @columnsFinal nvarchar(max) =''

Select @columnsStatus = @columnsStatus + N'[' + StatusName + N'],' from ##StatusSequence
Select @columnsEntered = @columnsEntered + N'[' + EnteredName + N'],' from ##StatusSequence
Select @columnsFinal = @columnsFinal + N'[' + StatusName + N'],' + N'[' + EnteredName + N'],' 
from ##StatusSequence

执行

SET  @sql = N';WITH Status AS
(
select JobNo,' +  Left(@columnsStatus, Len(@columnsStatus) - 1) + N' from (
select a.JobNo,b.StatusName,   a.Status  from ##tmpJobStatusLog a left join ##StatusSequence b on a.Sequence = b.Sequence
 ) as St pivot ( max(Status) for StatusName in ( ' +  Left(@columnsStatus, Len(@columnsStatus) - 1) + N') ) pvt),
Entered AS (select JobNo,' +  Left(@columnsEntered, Len(@columnsEntered) - 1) + N' from (
select a.JobNo,b.EnteredName,   a.Entered  from ##tmpJobStatusLog a left join ##StatusSequence b on a.Sequence = b.Sequence
 ) as St pivot ( max(Entered) for EnteredName in ( ' +  Left(@columnsEntered, Len(@columnsEntered) - 1) + N') ) pvt
 )
SELECT A.JobNo ,' + Left(@columnsFinal, Len(@columnsFinal) - 1) + N' FROM 
    Status A
INNER JOIN Entered B
On
A.JobNo = B.JobNo'

exec sp_executesql @sql

结果

JobNo       Status1     Entered1                Status2     Entered2                Status3     Entered3                Status4     Entered4                Status5     Entered5
----------- ----------- ----------------------- ----------- ----------------------- ----------- ----------------------- ----------- ----------------------- ----------- -----------------------
420964      801         2017-09-20 17:02:16.830 NULL        NULL                    NULL        NULL                    NULL        NULL                    NULL        NULL
420965      801         2017-09-20 17:05:29.130 803         2017-09-20 17:05:30.870 NULL        NULL                    NULL        NULL                    NULL        NULL
420966      801         2017-09-20 17:40:22.580 NULL        NULL                    NULL        NULL                    NULL        NULL                    NULL        NULL
420967      801         2017-09-20 17:47:52.340 NULL        NULL                    NULL        NULL                    NULL        NULL                    NULL        NULL
420968      801         2017-09-20 17:55:02.660 NULL        NULL                    NULL        NULL                    NULL        NULL                    NULL        NULL
420969      801         2017-09-08 18:18:16.490 802         2017-09-14 09:35:57.337 803         2017-09-14 10:13:25.733 812         2017-09-15 08:20:48.390 801         2017-09-20 17:58:18.503