2017-09-21 1 views
0

SQL Server 2012. J'ai besoin de créer une requête pour déterminer quels travaux étaient dans quel état à une date donnée dans le passé (BONUS: durée de leur statut.)SQL Convertir les lignes en colonnes pour suivre l'état du travail

J'ai une table de journal d'état du job avec les colonnes suivantes et structure:

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 

Je pense que je voudrais avoir un certain nombre d'emplois en particulier suivi par chaque état il était, et quand (de plus, je don ne vous souciez pas des remarques ou qui est entré dans le travail):

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 

... avec plus de colonnes après avoir indiqué 3Status et 3Enter, etc. J'ai seulement besoin de coder pour 8 statuts/dates saisies car c'est le nombre le plus élevé de fois qu'un travail est réorganisé ou remplacé dans l'état. S'il y a finalement plus de colonnes, je serai en mesure d'étendre toute réponse que je reçois ici pour inclure cette logique.

... parce que ma "réponse" éventuelle sera le 1er juillet 2016 (n'importe quelle date donnée): 87 postes étaient en 801 postes, 255 en 806 postes et 5 en 809 postes. En fait, j'ai besoin de faire des calculs pour déterminer à quel moment chaque emploi était dans un état particulier, mais comme c'est ma première question et je ne sais pas comment ma réponse sera compliquée, je l'appelle ici comme je l'ai fait. Je devine que je peux comprendre le reste une fois que je reçois ces statuts et dates columnar avec DateDiff.

J'ai essayé toutes les combinaisons que je peux penser de UNPIVOT, Lag/Lead, groupant, MAX, et cetera et ne peut obtenir nulle part.

À ce stade, il m'est même peut-être manquant quelque chose de simple et je me sentirai très bête à la réponse, mais je suis bel et bien coincé. Est-ce que je vais même dans le bon sens en essayant de faire sortir ces colonnes des rangées où elles se trouvent actuellement? Y a-t-il un moyen de prendre la date donnée et d'utiliser la table telle quelle? Si quelque chose n'est pas clair, je vais essayer de clarifier dans les mises à jour ou les réponses. À votre santé!

Et voici la réponse que j'ai choisi de @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 

Se exécute en 12 secondes contre plus d'un million de lignes de données sans gestion de la table temporaire nécessaire. ÉLÉGANT! Merci @Fercstar.

+0

Merci John Cappelletti pour le reformater! Beaucoup plus lisible. – DataVis4Fun

Répondre

0

Vous pouvez utiliser

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

Cela fera l'ordre des statuts par travail avec 1 étant le plus récent. Vous pouvez ensuite pivoter par cela.

Edition - requête complète basée sur des données d'échantillons

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 
+0

@ DataVis4Fun J'ai mis à jour ma réponse pour inclure une requête complète qui, en utilisant vos données d'exemple, renvoie les résultats souhaités. Vous pouvez calculer la différence entre les horodatages si vous voulez voir combien de temps il était dans chaque statut. – Fercstar

+0

Merci! C'est compressé, clair et je peux suivre ce que vous avez fait et développer. J'ai été capable d'ajouter facilement des colonnes supplémentaires et de passer à la mathématique de ce qui était où quand. Une solution élégante et excellente à mon problème. J'apprécie votre temps à répondre à cela. – DataVis4Fun

0

Si vous avez un num limité de statut, vous pouvez l'utiliser

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  

Résultat

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 
+0

Cela ne fonctionne que pour les deux premières colonnes et si elles sont un statut spécifique ... mais comment puis-je obtenir les troisième, quatrième, cinquième, etc colonnes de statut et entré? J'ai des statuts dont je me soucie: 806 807 808 809 et 813 avec la possibilité de n'importe quel travail étant dans n'importe quel statut d'abord, suivant ou dernier. J'ai 5 statuts et la possibilité d'utiliser chaque statut plusieurs fois, jusqu'à 8 colonnes sur la requête de résultats. Cela ne me sort pas d'une situation de codage difficile qui ne fonctionnera pas. – DataVis4Fun

+0

Je vais créer une autre réponse à cela. –

0

Je ne sais pas si c'est ce que vous voulez. J'ai créé un code qui obtient le JobNo et montre tous les statuts en fonction de l'heure.

Tables

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)) 

charge des données

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) 

Préparation des données

;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 

Préparation des noms de colonnes

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 

exécution

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 

Résultat

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 
+0

@ DataVis4Fun, ceci est la nouvelle réponse. –

+0

Pour calculer les données entre le statut, vous devez créer quelque chose comme je l'ai fait avec @columnsFinal –

+0

J'aime ça, mais quand je l'ai installé dans mon environnement de test, j'ai 53 états et j'ai entré des colonnes alors qu'il n'y a que 9 instances dans le Les données. J'ai essayé d'utiliser les colonnes "finales" que vous avez définies, mais je n'arrive pas à faire en sorte que seules les colonnes contenant des données apparaissent. Cela dit, j'ai appris un peu en utilisant et en modifiant votre code, alors MERCI! – DataVis4Fun