Gruppieren Sie nach Minuten- und Zwei-Minuten-Intervallen


8

Ich habe eine Datetime-Spalte und kann problemlos Abfragen mit einer Gruppe in meiner Datetime-Spalte ausführen. Ich möchte jedoch Gruppenabfragen für ausführen

  • Intervalle von 1 Minute
  • 2-Minuten-Intervalle
  • 5-Minuten-Intervalle
  • Intervalle von 1 Stunde
  • usw.

Wie mache ich das?


1
Alle in derselben Abfrage, oder soll die Abfrage bedingt nach einem bestimmten Intervall gruppiert werden? Wenn erstere, könnten Sie Beispieldaten und gewünschte Ergebnisse anzeigen?
Aaron Bertrand

Jede Gruppe nach Intervallen würde sich in einer anderen Abfrage befinden. Ich habe nur diese Intervalle als Beispiele ausgewählt. Die Idee ist, ich könnte die Abfrage erneut ausführen und leicht eine Klausel anpassen, um 2 Minuten, 2 Minuten, 5 Minuten usw. zu erhalten
Dublintech

1
Schauen Sie sich diese Antwort an. dba.stackexchange.com/questions/17669/…
Mikael Eriksson

Antworten:


8

Zunächst eine Tabelle und einige Beispieldaten zum Spielen:

USE tempdb;
GO
CREATE TABLE dbo.SomeTable(dt DATETIME);
GO
SET NOCOUNT ON;
GO
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -22, GETDATE());
GO 45
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -19, GETDATE());
GO 32
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -17, GETDATE());
GO 21
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -12, GETDATE());
GO 16
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -5, GETDATE());
GO 55
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -2, GETDATE());
GO 26
INSERT dbo.SomeTable(dt) SELECT DATEADD(MINUTE, -1, GETDATE());
GO 71
INSERT dbo.SomeTable(dt) SELECT GETDATE();
GO 14

(Ich würde dies in sqlfiddle tun, aber ich bin nicht sicher, ob es GO <int>viele Zeilen unterstützt und INSERT> 8000 Zeichen verschluckt .)

Nun eine gespeicherte Prozedur:

CREATE PROCEDURE dbo.GetGroupedIntervals
    @MinuteInterval TINYINT = 1
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE 
        @IntervalCount INT, @StartDate SMALLDATETIME;

    SELECT
        @StartDate = DATEADD(MINUTE, -1, MIN(dt)), 
        @IntervalCount = (DATEDIFF(MINUTE, MIN(dt), MAX(dt)) 
            + @MinuteInterval) / @MinuteInterval
          FROM dbo.SomeTable -- WHERE ...;

    ;WITH dates(s,e) AS
    (
        SELECT 
            DATEADD(MINUTE, @MinuteInterval*(n.n-1), @StartDate),
            DATEADD(MINUTE, @MinuteInterval*(n.n), @StartDate)
        FROM
        (
          SELECT 
            TOP (@IntervalCount) ROW_NUMBER() OVER (ORDER BY o.[object_id])
            FROM sys.all_objects AS o CROSS JOIN sys.all_columns AS c
            ORDER BY o.[object_id]
        ) AS n(n)
    )
    SELECT StartDate = d.s, c = COUNT(s.dt) 
    FROM dates AS d
    LEFT OUTER JOIN dbo.SomeTable AS s
        ON s.dt >= d.s AND s.dt < d.e
        -- AND any filter criteria for dbo.SomeTable?
    GROUP BY d.s
    ORDER BY d.s;
END
GO

Und einige Beispiele für die Verwendung:

EXEC dbo.GetGroupedIntervals @MinuteInterval = 1;
EXEC dbo.GetGroupedIntervals @MinuteInterval = 2;
EXEC dbo.GetGroupedIntervals @MinuteInterval = 5;

Der Kürze halber zeige ich die Ergebnisse für den letzten Anruf, aber Sie können mit den anderen spielen.

StartDate            c
-------------------  ----
2012-05-16 12:51:00  77
2012-05-16 12:56:00  21
2012-05-16 13:01:00  16
2012-05-16 13:06:00  55
2012-05-16 13:11:00  111

Einige Notizen:

  • Der Join mit s.dt ist wahrscheinlich leistungsfähiger als alle Extraktionsmethoden mit datepart, wenn die datetime-Spalte in Ihrer Basistabelle einen Index hat (oder möglicherweise in Zukunft).
  • Ich nahm an, Sie wollten alle Intervalle innerhalb des Bereichs anzeigen. Wenn Sie die Intervalle nicht mit 0 anzeigen möchten, ändern Sie einfach die linke äußere Verknüpfung in eine innere Verknüpfung.
  • Ich gehe eine Minute runter, falls das Startdatum bei der Konvertierung in SMALLDATETIME aufgerundet wurde. Für das 1-Minuten-Intervall und möglicherweise für andere kann dies zu 0 Zählungen für das erste Intervall führen. Sie können einstellen, wie diese Rundung erfolgt (z. B. können Sie FLOOR () verwenden, um sicherzustellen, dass sie immer abgerundet wird). Alles hängt davon ab, wie genau Sie sein müssen.
  • Ich habe keine WHERE-Klausel eingefügt, aber möglicherweise müssen Sie diese zum Filtern haben. Beispielsweise möchte Ihre Abfrage möglicherweise alle Intervalle für einen bestimmten Tag. Möglicherweise möchten Sie die Berechnung von ändern dates, um alle Intervalle für den Tag und nicht alle Intervalle zwischen der minimalen und der maximalen Zeit dbo.SomeTablean diesem Tag zu erstellen . Die folgende Variante sorgt dafür, dass die Daten für einen einzelnen Tag ab Mitternacht angezeigt und um @MinutInterval erhöht werden:

...

CREATE PROCEDURE dbo.GetGroupedIntervalsByDay
    @Date           DATE,
    @MinuteInterval TINYINT = 1
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE 
        @IntervalCount INT,
        @StartDate     SMALLDATETIME = @Date;

    SELECT
        @IntervalCount = 1440 / @MinuteInterval;

    ;WITH dates(s,e) AS
    (
        SELECT 
            DATEADD(MINUTE, @MinuteInterval*(n.n-1), @StartDate),
            DATEADD(MINUTE, @MinuteInterval*(n.n),   @StartDate)
        FROM
        (
          SELECT 
            TOP (@IntervalCount) ROW_NUMBER() OVER (ORDER BY o.[object_id])
            FROM sys.all_columns AS o
            ORDER BY o.[object_id]
        ) AS n(n)
    )
    SELECT StartDate = d.s, c = COUNT(s.dt) 
    FROM dates AS d
    LEFT OUTER JOIN dbo.SomeTable AS s
        ON s.dt >= d.s AND s.dt < d.e
        -- AND any filter criteria for dbo.SomeTable?
    GROUP BY d.s
    ORDER BY d.s;
END
GO

Beispielanrufe:

EXEC dbo.GetGroupedIntervalsByDay @Date = '20120516', @MinuteInterval = 1;
EXEC dbo.GetGroupedIntervalsByDay @Date = '20120516', @MinuteInterval = 2;
EXEC dbo.GetGroupedIntervalsByDay @Date = '20120516', @MinuteInterval = 5;

Verkürzte Ergebnisse des letzten Anrufs:

StartDate             c
-------------------   ----
2012-05-16 00:00:00   0
2012-05-16 00:05:00   0
2012-05-16 00:10:00   0
...
2012-05-16 12:40:00   0
2012-05-16 12:45:00   0
2012-05-16 12:50:00   45
2012-05-16 12:55:00   53
2012-05-16 13:00:00   16
2012-05-16 13:05:00   55
2012-05-16 13:10:00   111
2012-05-16 13:15:00   0
2012-05-16 13:20:00   0
...
2012-05-16 23:45:00   0
2012-05-16 23:50:00   0
2012-05-16 23:55:00   0

(Wenn Sie die Intervalle nicht ohne Zählung einschließen möchten, ändern Sie die linke äußere Verknüpfung in eine innere Verknüpfung. Sie können auch seltsame Ergebnisse erzielen, wenn Sie ein Intervall auswählen, das nicht gut in 1440 passt. I ' Überlasse diesen Fall dem Leser als Übung.)


Vielen Dank für die Hinweise zu GO <int> - ich wusste nicht einmal, dass dies eine Option für GO ist. Ich werde es der Liste der Verbesserungen für SQL Fiddle (meine Site) hinzufügen.
Jake Feasel

2
declare @T table
(
  Value datetime
);

insert into @T values ('2012-01-01T00:02:00');
insert into @T values ('2012-01-01T00:03:00');
insert into @T values ('2012-01-01T00:04:00');
insert into @T values ('2012-01-01T00:05:00');
insert into @T values ('2012-01-01T00:06:00');
insert into @T values ('2012-01-01T00:07:00');
insert into @T values ('2012-01-01T00:08:00');
insert into @T values ('2012-01-01T00:09:00');
insert into @T values ('2012-01-01T00:10:00');
insert into @T values ('2012-01-01T00:11:00');

-- Interval in minutes    
declare @Interval int
set @Interval = 4

select count(*) as "Count",
       dateadd(minute, (datediff(minute, 0, Value) / @Interval) * @Interval, 0) as "Value"
from @T
group by dateadd(minute, (datediff(minute, 0, Value) / @Interval) * @Interval, 0);

Ergebnis:

Count       Value
----------- -----------------------
2           2012-01-01 00:00:00.000
4           2012-01-01 00:04:00.000
4           2012-01-01 00:08:00.000

0

Da DATEPARTgibt ganze Zahlen zurück ...

SELECT
    (DATEPART(MINUTE, StartDate) / 5) * 5, COUNT(*)
FROM
    Table
GROUP BY
    (DATEPART(MINUTE, StartDate) / 5) * 5
ORDER BY
    (DATEPART(MINUTE, StartDate) / 5) * 5

Die angezeigten Zahlen sind der Beginn des Fünf-Minuten-Zeitraums. Die mit "30" bezeichnete Zeile deckt also Ereignisse ab, die bei X: 30, X: 31, X: 32, X: 33 und X: 34 auftreten.


Warum multiplizieren und dividieren Sie mit 5. Heben sie sich gegenseitig auf?
Dublintech

1
Ja, aber da keine Brüche erhalten bleiben, wird das Nettoergebnis auf das nächste Vielfache von fünf abgerundet.
Jon of All Trades

2
Wird diese Gruppe nicht stundenlang zusammen sein? 4:30wird mit 5:30und kombiniert, 6:30da die Minute auf der ganzen Linie gleich ist. Sie müssen mindestens auch Datum und Uhrzeit angeben.
JNK

Wenn das Teil des OP-Ziels ist, dann sicherlich ja. Diese Abfrage erfüllt die Kriterien "Gruppenabfragen für X-Minuten-Intervalle". Erweitern Sie es nach Bedarf.
Jon of All Trades

Sicher, wenn die where-Klausel alle Daten auf eine Stunde beschränkt, erfüllt sie die Kriterien. Ich denke, das ist weder in der Frage noch in Ihrer Antwort explizit genug. MEINER BESCHEIDENEN MEINUNG NACH.
Aaron Bertrand
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.