1
votes

Tableau croisé dynamique indiquant le nombre d'enregistrements «ouverts» par heure

J'ai effectivement une table avec deux champs importants. ReadyDate et CompleteDate.

Je veux que ma sortie me donne 24 colonnes, correspondant à une heure de la journée, chacune me donnant le nombre d'enregistrements qui étaient encore "ouverts" (définis comme ReadyDate avant la fin de l'heure mais CompleteDate après la fin d'une heure) pendant cette heure d'horloge.

L'astuce est donc qu'un enregistrement peut apparaître dans zéro ou plusieurs colonnes. Quelque chose qui était ReadyDate à 6h05 et CompleteDate 9h17 apparaîtrait dans la colonne 6: 00a, 7: 00a et 8: 00a.

Donc, si le tableau contient les entrées suivantes

< pre> XXX

Je m'attendrais à ce que ma sortie contienne 24 colonnes avec les nombres suivants 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0

Je ne sais pas comment procéder. Toutes les informations seraient appréciées


1 commentaires

Marquez votre question avec la base de données que vous utilisez.


3 Réponses :


1
votes

Ce n'est pas le plus joli mais cela fonctionne comme vous l'avez expliqué. J'adorerais voir une solution plus élégante si quelqu'un en a une.

0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22  23
0   0   0   0   0   0   0   1   1   0   1   1   2   1   1   0   0   0   0   0   0   0   0   0

Sortie:

DECLARE @t TABLE
(
    date1 DATETIME NOT NULL,
    date2 DATETIME NOT NULL
);

INSERT INTO @t
(
    date1,
    date2
)
VALUES
('10-14-2019 06:05', '10-14-2019 06:10'),
('10-14-2019 07:12', '10-14-2019 09:30'),
('10-14-2019 10:02', '10-14-2019 13:55'),
('10-14-2019 12:50', '10-14-2019 15:30');

SELECT SUM(t.[0]) AS [0],
       SUM(t.[1]) AS [1],
       SUM(t.[2]) AS [2],
       SUM(t.[3]) AS [3],
       SUM(t.[4]) AS [4],
       SUM(t.[5]) AS [5],
       SUM(t.[6]) AS [6],
       SUM(t.[7]) AS [7],
       SUM(t.[8]) AS [8],
       SUM(t.[9]) AS [9],
       SUM(t.[10]) AS [10],
       SUM(t.[11]) AS [11],
       SUM(t.[12]) AS [12],
       SUM(t.[13]) AS [13],
       SUM(t.[14]) AS [14],
       SUM(t.[15]) AS [15],
       SUM(t.[16]) AS [16],
       SUM(t.[17]) AS [17],
       SUM(t.[18]) AS [18],
       SUM(t.[19]) AS [19],
       SUM(t.[20]) AS [20],
       SUM(t.[21]) AS [21],
       SUM(t.[22]) AS [22],
       SUM(t.[23]) AS [23]
FROM
(
    SELECT id,
           date1,
           date2,
           CASE
               WHEN 0
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [0],
           CASE
               WHEN 1
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [1],
           CASE
               WHEN 2
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [2],
           CASE
               WHEN 3
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [3],
           CASE
               WHEN 4
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [4],
           CASE
               WHEN 5
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [5],
           CASE
               WHEN 6
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [6],
           CASE
               WHEN 7
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [7],
           CASE
               WHEN 8
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [8],
           CASE
               WHEN 9
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [9],
           CASE
               WHEN 10
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [10],
           CASE
               WHEN 11
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [11],
           CASE
               WHEN 12
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [12],
           CASE
               WHEN 13
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [13],
           CASE
               WHEN 14
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [14],
           CASE
               WHEN 15
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [15],
           CASE
               WHEN 16
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [16],
           CASE
               WHEN 17
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [17],
           CASE
               WHEN 18
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [18],
           CASE
               WHEN 19
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [19],
           CASE
               WHEN 20
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [20],
           CASE
               WHEN 21
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [21],
           CASE
               WHEN 22
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [22],
           CASE
               WHEN 23
                    BETWEEN DATEPART(HOUR, date1) AND DATEPART(HOUR, date2) - 1 THEN
                   1
               ELSE
                   0
           END AS [23]
    FROM @t
) t;


1 commentaires

Le problème avec ceci est que si les heures passent au-dessus de minuit, cela ne fonctionnera pas comme prévu. Votre ensemble de données aurait-il des résultats comme celui-ci?



2
votes

MODIFICATION CORRECTE pour SHIFT OF 1 HEURE

Voici une option PIVOT qui utilise un tableau de pointage ad-hoc.

Le le report de minuit est pris en compte.

Vous ne savez pas si vous vouliez des lignes par date. Sinon, ce serait une petite question à supprimer.

Exemple

;with cte1 as (
    Select Date  = convert(date,D) 
          ,Item  = datepart(hour,D)
          ,Value = 1
     From  YourTable A
     Cross Apply (
                    Select Top (DateDiff(HOUR,ReadyDate,CompleteDate)+0) 
                           D=DateAdd(HOUR,-1+Row_Number() Over (Order By (Select Null)),ReadyDate) 
                     From  master..spt_values n1,master..spt_values n2
                  ) B
),  cte2 as ( 
    Select Date
          ,B.Item 
          ,B.Value
     From  (Select distinct Date from cte1 ) a
     Cross Join (Select Top 24 Item=-1+Row_Number() Over (Order By (Select NULL)),Value=0 From master..spt_values n1 ) B
)
Select *
 From  (Select * from cte1
        Union All
        Select * from cte2
        ) src
 Pivot (sum(Value) for Item in ([0] ,[1] ,[2] ,[3] ,[4] ,[5] ,[6] ,[7] ,[8] ,[9] ,[10],[11],
                                [12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23]) ) pvt

Retours

 entrez la description de l'image ici


3 commentaires

Votre solution est géniale et je l'apprécie beaucoup. Je travaille toujours sur l'exemple et je pense en comprendre l'essentiel. J'ai dû ajouter ma propre table remplie de valeurs de 0 à 23 car je n'ai pas accès à la base de données principale. Cependant, cela fonctionne extrêmement bien. J'essaye de comprendre la première croix s'appliquent. D'une manière ou d'une autre, il utilise la table des valeurs deux fois. J'ai divisé cette partie en une sélection distincte pour la comprendre. Cela fonctionne, mais je ne suis pas compétitif dans la logique. Pouvez-vous expliquer?


@AbrahamLincoln Heureux que cela ait aidé. CTE1 «étendra» chaque ligne via un tableau de pointage / nombres ad hoc. CTE2 créera une liste factice de toutes les combinaisons de dates et d'heures ... c'est une petite astuce pour éviter les valeurs NULL dans le PIVOT final. La sélection finale est un simple PIVOT de cte1 et cte2


@AbrahamLincoln Je devrais ajouter ... BIEN FAIT en prenant l'initiative de le décomposer, c'est ainsi que nous apprenons :)



0
votes

Il y a quelques questions sur l'échantillon de données que vous avez fourni. Il n'est pas tout à fait clair comment chaque heure est sélectionnée. Une heure est-elle sélectionnée si le nombre de minutes pendant lesquelles un élément était actif est supérieur à un certain nombre? Il y a également un problème possible avec CompletedDate le jour suivant. Dans les deux cas, voici un autre exemple de comment y parvenir. Vous devrez bricoler la logique des heures pour obtenir la réponse dont vous avez besoin.

DECLARE @data Table
(
    id int identity(1,1)
    ,ReadyDate datetime
    ,CompletedDate datetime
    ,foo varchar(500)
)
INSERT INTO @data
VALUES

('10-14-2019 06:05','10-14-2019 06:10','(this will populate no columns)')
,('10-14-2019 07:12','10-14-2019 09:30','(this will populate the 7:00 hour and 8:00 hour)')
,('10-14-2019 10:02','10-14-2019 13:55','(this will populate the 10:00 hour, 11:00, 12:00)')
,('10-14-2019 12:50','10-14-2019 15:30','(this will populate the 12:00, 13:00, 14:00)') 

;
;WITH Numbers AS
(
    SELECT 1 AS Number
    UNION ALL
    SELECT Number+1
        FROM Numbers 
        WHERE Number < 24
)
,src
as (
Select *, 
    flag = CASE WHEN Number >= DATEPART(hour, ReadyDate) AND Number < DATEPART(hour, ReadyDate) + DateDiff(hour, ReadyDate, CompletedDate) THEN 1 ELSE 0 END 
from @Data
    CROSS APPLY Numbers
)
Select * 
from src
    PIVOT 
    (
        max(flag) for number in ([1],[2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24])
    ) as ptable


0 commentaires