2016-07-18 1 views
0

J'ai une table temporaire contenant 24 entrées, une pour chaque heure d'un jour spécifique annotée par DayID. La table contient une indication indiquant si une application est activée ou désactivée pendant cette période spécifique.Serveur SQL: comment construire une plage de dates à partir d'une table avec les heures de début et de fin

Je voudrais réduire le nombre d'entrées dans la table pour afficher uniquement les heures pendant lesquelles l'application doit être activée.

Certaines données d'un échantillon:

CREATE TABLE #OnOff 
(
    RowID INT identity(1, 1), 
    DayID TINYINT, 
    OnOff BIT, 
    StartTime TIME, 
    EndTime TIME 
) 

INSERT INTO #OnOff (DayID, OnOff, StartTime, EndTime) VALUES 
(1, 0, '00:00', '00:59'), 
(1, 0, '01:00', '01:59'), 
(1, 0, '02:00', '02:59'), 
(1, 1, '03:00', '03:59'), 
(1, 1, '04:00', '04:59'), 
(1, 0, '05:00', '05:59'), 
(1, 1, '06:00', '06:59'), 
(1, 1, '07:00', '07:59'), 
(1, 0, '08:00', '08:59'), 
(1, 0, '09:00', '09:59'), 
(1, 0, '10:00', '10:59'), 
(1, 0, '11:00', '11:59'), 
(1, 0, '12:00', '12:59'), 
(1, 0, '13:00', '13:59'), 
(1, 1, '14:00', '14:59'), 
(1, 1, '15:00', '15:59'), 
(1, 1, '16:00', '16:59'), 
(1, 0, '17:00', '17:59'), 
(1, 0, '18:00', '18:59'), 
(1, 0, '19:00', '19:59'), 
(1, 0, '20:00', '20:59'), 
(1, 0, '21:00', '21:59'), 
(1, 0, '22:00', '22:59'), 
(1, 0, '23:00', '23:59') 

La sortie souhaitée doit être

DayID StartTime EndTime 
1  03:00  04:59 
1  06:00  07:59 
1  14:00  16:59 

Le tableau de #OnOff contiendra toujours une valeur pour toutes les heures de la journée (c.-à-fin et les heures de départ seront toujours être consécutifs, sauf pour la dernière heure). On peut évidemment y parvenir avec un curseur, mais cela semble assez inefficace. Y a-t-il un meilleur moyen d'atteindre ce résultat?

+0

Votre question n'est pas très claire. pourquoi ne pas simplement insérer seulement les lignes où la valeur de 'OnOff' est 1? –

+0

Méfiez-vous des plages horaires que vous avez montrées .... 'timenow <= '16: 59'' diffère de' timenow <17: 00' de près d'une minute. –

+0

@Ollie Merci de votre commentaire ... bien noté. Dans ce cas, la fréquence n'est jamais inférieure à 60s et peut être gérée, mais je suis d'accord avec le principe soulevé – Gert

Répondre

2

Vous pouvez obtenir les résultats souhaités à l'aide d'un CTE sur l'ensemble de données:

CREATE TABLE #OnOff 
    (
     RowID INT IDENTITY(1, 1) , 
     DayID TINYINT , 
     OnOff BIT , 
     StartTime TIME , 
     EndTime TIME 
    ) 

INSERT INTO #OnOff 
     (DayID, OnOff, StartTime, EndTime) 
VALUES (1, 0, '00:00', '00:59'), 
     (1, 0, '01:00', '01:59'), 
     (1, 0, '02:00', '02:59'), 
     (1, 1, '03:00', '03:59'), 
     (1, 1, '04:00', '04:59'), 
     (1, 0, '05:00', '05:59'), 
     (1, 1, '06:00', '06:59'), 
     (1, 1, '07:00', '07:59'), 
     (1, 0, '08:00', '08:59'), 
     (1, 0, '09:00', '09:59'), 
     (1, 0, '10:00', '10:59'), 
     (1, 0, '11:00', '11:59'), 
     (1, 0, '12:00', '12:59'), 
     (1, 0, '13:00', '13:59'), 
     (1, 1, '14:00', '14:59'), 
     (1, 1, '15:00', '15:59'), 
     (1, 1, '16:00', '16:59'), 
     (1, 0, '17:00', '17:59'), 
     (1, 0, '18:00', '18:59'), 
     (1, 0, '19:00', '19:59'), 
     (1, 0, '20:00', '20:59'), 
     (1, 0, '21:00', '21:59'), 
     (1, 0, '22:00', '22:59'), 
     (1, 0, '23:00', '23:59') 

;WITH cte 
    AS (-- Get the first row, seed for the cte, set the OnOffGroup to 1 
     SELECT TOP 1 
       RowID , DayID , OnOff , StartTime , EndTime , 1 AS OnOffGroup 
     FROM  #OnOff 
     WHERE OnOff = 1 
     ORDER BY RowID 
     UNION ALL 
     -- Join latest cte row with next row in sequence, 
     -- OnOffGroup set to previous row value if state matches, otherwise increment 
     SELECT #OnOff.RowID , #OnOff.DayID , #OnOff.OnOff , 
       #OnOff.StartTime , #OnOff.EndTime , 
       CASE WHEN #OnOff.OnOff = 1 THEN cte.OnOffGroup 
         ELSE cte.OnOffGroup + 1 
       END AS OnOffGroup 
     FROM  #OnOff 
       INNER JOIN cte ON cte.RowID + 1 = #OnOff.RowID 
     ) 
    -- Output results with grouping and min/max to produce desired results 
    SELECT cte.DayID , 
      MIN(cte.StartTime) AS StartTime , 
      MAX(cte.EndTime) AS EndTime , 
      cte.OnOffGroup 
    FROM cte 
    WHERE cte.OnOff = 1 
    GROUP BY cte.DayID , 
      cte.OnOffGroup 

Produit:

DayID StartTime   EndTime    OnOffGroup 
1  03:00:00.0000000 04:59:00.0000000 1 
1  06:00:00.0000000 07:59:00.0000000 2 
1  14:00:00.0000000 16:59:00.0000000 8 
+0

Merci pour votre aide. Cela a bien fonctionné. – Gert

1

Une autre façon avec OUTER APPLY et DENSE_RANK:

;WITH cte AS (
SELECT o.StartTime, 
     o.EndTime, 
     DENSE_RANK() OVER (ORDER BY r.EndTime) as DR 
FROM #OnOff o 
OUTER APPLY (
      SELECT top 1 * 
      FROM #OnOff 
      WHERE o.EndTime < EndTime and OnOff = 0 
      ) as r 
WHERE o.OnOff = 1 
) 

SELECT MIN(StartTime) as StartTime, 
     MAX(EndTime) as EndTime 
FROM cte 
GROUP BY DR 

sortie :

StartTime   EndTime 
03:00:00.0000000 04:59:59.0000000 
06:00:00.0000000 07:59:59.0000000 
14:00:00.0000000 16:59:59.0000000 
+0

Merci pour votre aide. Cela a bien fonctionné et était assez facile d'ajouter des jours supplémentaires pour les autres jours. – Gert