2016-07-18 10 views
0

Ich habe eine temporäre Tabelle, die 24 Einträge enthält, einen für jede Stunde eines bestimmten Tages, der von DayID kommentiert wird. Die Tabelle enthält eine Angabe, ob eine Anwendung während dieses bestimmten Zeitraums aktiviert oder deaktiviert ist.SQL Server: Wie man einen Datumsbereich von der Tabelle mit Anfangs- und Endezeiten erstellt

Ich möchte die Anzahl der Einträge in der Tabelle reduzieren, um nur die Zeiten anzuzeigen, in denen die Anwendung eingeschaltet sein sollte.

Einige Beispieldaten folgen:

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

Der gewünschte Ausgang

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

Die #OnOff Tabelle einen Wert für jede Stunde des Tages (dh Ende und Startzeiten enthalten wird es immer sein soll, werden immer hintereinander sein, außer für die letzte Stunde). Man kann das natürlich mit einem Cursor erreichen, aber das scheint ziemlich ineffizient zu sein. Gibt es einen besseren Weg, um dieses Ergebnis zu erreichen?

+0

Ihre Frage ist nicht sehr klar. warum nicht einfach nur die Zeilen einfügen, in denen der Wert von On On 1 ist? –

+0

Vorsicht mit den Zeitbereichen, die Sie gezeigt haben .... 'timenow <= '16: 59'' unterscheidet sich von' timenow <17: 00 'um fast eine Minute. –

+0

@Ollie Danke deinen Kommentar ... gut bemerkt. In diesem Fall ist die Frequenz nie kleiner als 60s und kann gemanagt werden, aber ich stimme dem Prinzip zu. – Gert

Antwort

2

Sie können die gewünschten Ergebnisse erzielen durch einen CTE über den Datensatz mit:

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 

Produziert:

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

Danke für Ihre Hilfe. Das hat gut funktioniert. – Gert

1

Eine andere Art und Weise mit OUTER APPLY und 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 

Ausgang :

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

Danke für Ihre Hilfe. Dies funktionierte gut und es war ziemlich einfach, zusätzliche dayIDs für andere Tage hinzuzufügen. – Gert