2016-07-01 7 views
1

Versuchen, eine Abfrage zu erstellen.Finden Sie Min und Max für Teilmengen von aufeinanderfolgenden Zeilen - Lücken und Inseln

Die Eingabe wird nach Zeilennummer in Spalte 'rn' sortiert, beginnend mit 1 für jeden eindeutigen Wert in 'name' und definiert eine bestimmte Sequenz von Einträgen in 'act'. In der Spalte 'act' hält es zwei Werte in mehreren Vorkommen,> schlafen < und> wecken <. Das Ziel besteht darin, für jeden aufeinanderfolgenden Satz von Zeilen eines dieser Werte den minimalen und maximalen Wert von startt und ended zu finden.

Dies ist der Eingang sein:

name  act  rn  startt endd 
---------- ---------- ------ ------ ------ 
jimmy  sleep  1  1  3 
jimmy  wake  2  4  7 
jimmy  wake  3  8  10 
jimmy  sleep  4  11  13 
karen  wake  1  1  4 
karen  sleep  2  5  7 
karen  wake  3  8  9 
karen  wake  4  10  12 
karen  wake  5  13  14 
karen  sleep  6  15  17 
karen  sleep  7  18  20 

der gewünschte Ausgang:

name  act  startt endd 
---------- ---------- ------ ------ 
jimmy  sleep  1  3  
jimmy  wake  4  10  
jimmy  sleep  11  13  
karen  wake  1  4  
karen  sleep  5  7  
karen  wake  8  14  
karen  sleep  15  20 

Die Quelle des Eingangs bietet keine weiteren Spalten. Die Anzahl der Mitglieder in jeder Teilmenge kann sehr viel höher sein als in diesem Beispiel.

Ich habe verschiedene Arten der Aggregation versucht, aber keine funktionierte. Ich glaube, mit LEAD und LAGG und weitere Tricks kann mich dorthin bringen, aber das scheint schrecklich unelegant. Ich habe den Gedanken, dass es der Schlüssel ist, jede Teilmenge zu unterscheiden, d. H. Einen Identifizierer zu erzeugen, der für alle seine Mitglieder einzigartig ist. Mit diesem ist ein Aggregat mit min und max einfach. Vielleicht bin ich falsch. Vielleicht ist es unmöglich. Vielleicht ein Self-Join. Vielleicht eine rekursive Cte. Ich weiß es nicht.

Also: weiß jemand, wie man das bekommt? Hilfe wird sehr geschätzt.

UPDATE:

Danke zu Gordon Linoff, shawnt00 und den anderen Mitwirkenden, die kommentiert. Mit Ihrem Rat habe ich große Lücken in meinem Werkzeugkasten der Logik geschlossen.

für alle Interessenten:

declare @t table (
    name nvarchar(10) 
    ,act nvarchar (10) 
    ,startt smallint 
    ,endd smallint 
    ) 

insert into @t (
    name 
    ,act 
    ,startt 
    ,endd 
    ) 
values 
    ('jimmy','sleep', 1,3) 
    ,('jimmy','wake', 4,7) 
    ,('jimmy','wake', 8,10) 
    ,('jimmy','sleep', 11,13) 
    ,('karen','wake', 1,4) 
    ,('karen','sleep', 5,7) 
    ,('karen','wake', 8,9) 
    ,('karen','wake', 10,12) 
    ,('karen','wake', 13,14) 
    ,('karen','sleep', 15,17) 
    ,('karen','sleep', 18,20) 

; --- all rows, no aggregating 
with 
cte as (
select 
    name 
    ,act 
    ,row_number() over (partition by name order by name,startt) rn 
    ,row_number() over (partition by name, act order by name,startt) act_n 
    ,startt 
    ,endd 
from 
    @t) 
select 
    name 
    ,act 
    ,startt 
    ,endd 
    ,rn 
    ,act_n 
    ,rn - act_n diff 
from 
    cte 
order by 
    name 
    ,rn 

;--- aggregating for the desired ouput 
with 
cte as (
select 
    name 
    ,act 
    ,row_number() over (partition by name order by name,startt) rn 
    ,row_number() over (partition by name, act order by name,startt) act_n 
    ,startt 
    ,endd 
from 
    @t) 
select 
    name 
    ,act 
    ,min(startt) startt 
    ,max(endd) endd 
    ,min(rn)  min_rn 
    ,max(rn)  max_rn 
from 
    cte 
group by 
    name 
    ,act 
    ,rn - act_n 
order by 
    name 
    ,min(rn) 
+0

Dies ist ein Gaps and Islands-Problem, suchen Sie danach und Sie finden Lösungsvorschläge. Oder sehen Sie sich das Kapitel 5 PDF auf dieser Seite an https://www.manning.com/books/sql-server-mvp-deep-dives – mheptinstall

+0

Also, welches RDBMS? – Strawberry

+0

Wenn der Unterschied zwischen Ende und Start immer 1 ist, wenn sie zusammen gruppiert werden sollen? Oder sind das nur Beispielsätze und der nächste Satz basiert auf den Namensänderungen, act? –

Antwort

3

Sie wollen aufeinander folgende Gruppen von ähnlichen Reihen zu finden und dann Aggregation. Ich mag den Unterschied von Zeilennummern Ansatz:

select name, act, min(startt) as startt, max(endd) as endd 
from (select i.*, 
      row_number() over (partition by name, act order by rn) as seqnum_na, 
      row_number() over (partition by name order by rn) as seqnum_n 
     from input i 
    ) i 
group by (seqnum_n - seqnum_na), name, act; 

Sie können sehen, wie das funktioniert, indem man, was die Unterabfrage der Fall ist.

+0

überprüfen Sie gerahmte Fensterfunktionen.Du solltest das alles in einem Schuss machen können. https://msdn.microsoft.com/en-us/library/ms189461.aspx –

+1

@MatthewWhited vielleicht könnten Sie eine andere Antwort mit dieser Lösung schreiben. Ich verstehe immer noch nicht das Konzept der Reihe, auch wenn ich es ein paar Mal probiere. –

+0

Ich glaube, ich habe zu früh auf Frames gesprochen. Ich habe sie für Statistiken verwendet, aber das ist ein bisschen anders. es sieht eher wie ein rekursiver CTE aus. (oder eine Gruppe wie dieses Beispiel) ... aber wenn ich meinen Fuß aus meinem Mund ziehe, werde ich eine Antwort posten. –

1

Dies setzt voraus, dass Sie keine Lücken in der rn Nummerierung haben, so dass es nicht erneut berechnet wird.

with T2 as (
    select *, row_number() over (partition by name, act order by rn) as grp_rn 
    from T 
) 
select name, act, min(startt) as startt, max(endd) as endd 
from T2 
group by name, act, rn - grp_rn 
order by name, startt; 

http://rextester.com/CCQJJ93990

Dies ist eine typische Lücken und Inseln Abfrage. Der Schlüssel hier ist, dass, wenn Sie einen Cluster von Zeilen haben, die zwei verschiedenen Zahlen in Schritt erhöht werden, und das bedeutet, dass der Unterschied eine Konstante für den Cluster sein wird. Dieser Unterschied erhöht sich, wenn Sie sich in der Liste nach unten arbeiten.

+0

Vielen Dank für Ihre Erklärung. Dieser Trick ist nett. Auch ein guter Hinweis auf die Zunahme der Differenz. –