2015-03-29 4 views
6

Ich habe eine Tabelle, in der Nachrichten gespeichert werden, während sie passieren. Normalerweise gibt es eine Nachricht "A" und manchmal sind die A's durch eine einzige Nachricht "B" getrennt. Nun möchte ich die Werte gruppieren, damit ich sie analysieren kann, zum Beispiel den längsten "A" -Streifen oder die Verteilung von "A" -Streifen zu finden.Gruppieren und Zählen von Zeilen nach Wert, bis sie sich ändert

Ich habe bereits eine COUNT-OVER-Abfrage versucht, die aber für jede Nachricht weiter zählt.

SELECT message, COUNT(*) OVER (ORDER BY Timestamp RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 

Dies ist mein Beispieldaten:

Timestamp  Message 
20150329 00:00 A 
20150329 00:01 A 
20150329 00:02 B 
20150329 00:03 A 
20150329 00:04 A 
20150329 00:05 A 
20150329 00:06 B 

Ich möchte folgende Ausgabe

Message COUNT 
A   2 
B   1 
A   3 
B   1 
+0

Also sind hier zwei Spalten beteiligt, Nachricht und Zeitstempel? – jarlh

+0

Es gibt eine Zeitstempelspalte, aber die Daten werden der Reihe nach gespeichert. – dwonisch

+0

Betrachten Sie Daten immer als ungeordnet! (Selbst wenn es gerade bestellt zu sein scheint, kann es sich in der Zukunft ändern.) Schreiben Sie niemals Anfragen in Abhängigkeit von einer impliziten Reihenfolge !!! – jarlh

Antwort

7

Das war interessant :)

;WITH cte as (
SELECT Messages.Message, Timestamp, 
ROW_NUMBER() OVER(PARTITION BY Message ORDER BY Timestamp) AS gn, 
ROW_NUMBER() OVER (ORDER BY Timestamp) AS rn 
FROM Messages 
), cte2 AS (
SELECT Message, Timestamp, gn, rn, gn - rn as gb 
FROM cte 
), cte3 AS (
SELECT Message, MIN(Timestamp) As Ts, COUNT(1) as Cnt 
FROM cte2 
GROUP BY Message, gb) 
SELECT Message, Cnt FROM cte3 
ORDER BY Ts 

Hier ist der Satz Ergebnis ist:

Die Abfrage kann kürzer sein, aber ich poste es auf diese Weise, damit Sie sehen können, was passiert. Das Ergebnis ist genau wie gewünscht. Dies ist der wichtigste Teil gn - rn die Idee ist, die Zeilen in jeder Partition zu nummerieren und gleichzeitig die Zeilen in der ganzen Reihe, dann wenn Sie die eine von der anderen abziehen, erhalten Sie den "Rang" jeder Gruppe.

;WITH cte as (
SELECT Messages.Message, Timestamp, 
ROW_NUMBER() OVER(PARTITION BY Message ORDER BY Timestamp) AS gn, 
ROW_NUMBER() OVER (ORDER BY Timestamp) AS rn 
FROM Messages 
), cte2 AS (
SELECT Message, Timestamp, gn, rn, gn - rn as gb 
FROM cte 
) 
SELECT * FROM cte2 

Message Timestamp   gn rn gb 
A 2015-03-29 00:00:00.000 1 1 0 
A 2015-03-29 00:01:00.000 2 2 0 
B 2015-03-29 00:02:00.000 1 3 -2 
A 2015-03-29 00:03:00.000 3 4 -1 
A 2015-03-29 00:04:00.000 4 5 -1 
A 2015-03-29 00:05:00.000 5 6 -1 
B 2015-03-29 00:06:00.000 2 7 -5 
+0

Ist Rn vom ersten CTE tatsächlich im zweiten CTE verfügbar? – Mihai

+0

Es funktioniert langsam aber perfekt (aber ich habe viel Zeit für diese Abfrage). Also ja es ist verfügbar. – dwonisch

+0

@Mihai Entschuldigung, ich verstehe deine Frage nicht. –

3

Hier ist eine wenig kleine Lösung:

DECLARE @t TABLE (d DATE, m CHAR(1)) 

INSERT INTO @t 
VALUES ('20150301', 'A'), 
     ('20150302', 'A'), 
     ('20150303', 'B'), 
     ('20150304', 'A'), 
     ('20150305', 'A'), 
     ('20150306', 'A'), 
     ('20150307', 'B'); 

WITH 
c1 AS(SELECT d, m, IIF(LAG(m, 1, m) OVER(ORDER BY d) = m, 0, 1) AS n FROM @t), 
c2 AS(SELECT m, SUM(n) OVER(ORDER BY d) AS n FROM c1) 
    SELECT m, COUNT(*) AS c 
    FROM c2 
    GROUP BY m, n 

Ausgang:

m c 
A 2 
B 1 
A 3 
B 1 

Die Idee ist, Wert 1 in den Zeilen zu erhalten, wo Nachricht geändert wird:

2015-03-01 A 0 
2015-03-02 A 0 
2015-03-03 B 1 
2015-03-04 A 1 
2015-03-05 A 0 
2015-03-06 A 0 
2015-03-07 B 1 

Th e zweite Schritt ist nur Summe der aktuellen Zeilenwert + alle vorherigen Werte:

2015-03-01 A 0 
2015-03-02 A 0 
2015-03-03 B 1 
2015-03-04 A 2 
2015-03-05 A 2 
2015-03-06 A 2 
2015-03-07 B 3 

Auf diese Weise youbymessage Spalte Gruppierung Sätze erhalten und berechnete Spalte.

Verwandte Themen