# Oracle - aufeinanderfolgende Zeilen zusammenfassen



## warface (12. März 2020)

Hallo,
ich hoffe ihr könnt mir weiter helfen.
ich habe eine Tabelle mit den folgenden Spalten
- Gruppe
- Name
- Datum
- Von
- bis
- Menge
und würde gerne die Einträge zusammenfassen solange die Datensätze auf einander folgen.

z.B.
Fest;Mustermann;20200312;900;1000;50
Fest;Mustermann;20200312;1000;1100;40
Fest;Mustermann;20200312;1100;1200;50
Fest;Mustermann;20200312;1210;1300;50
Fest;Mustermann;20200312;1300;1400;50

Ergebnis
Fest;Mustermann;20200312;900;1200;140
Fest;Mustermann;20200312;1210;1400;100

Ich hab es schon mal mit der Lead-Funktion probiert, da es aber auch 100+ Datensätze sein könnten, müsste ich ja eine ewig lange Abfrage bauen.
Gibt es da vielleicht ein bessere Lösung?


----------



## Zvoni (12. März 2020)

Ich sehe hier kein Muster, wie sich das Resultat aufbauen soll, insbesondere nicht für die letzten beiden Spalten.
Ansonsten hat Oracle AFAIK die Funktion LISTAGG um Spalten zu einem String zu kombinieren
https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions089.htm#SQLRF30030
EDIT: OK, ich glaube ich habs verstanden. Hinweis war "aufeinander folgen".
Solange Von und Bis (kombiniert mit Datum?) "direkt" aufeinander folgen (im Gegensatz zu Bis 1200 - Von 1210) soll die letzte Spalte summiert werden.
Au weia, dat wird ein Monster....


----------



## warface (12. März 2020)

Zvoni
genau, ich will ein bestimmtes Feld summieren so lange die Datensätze aufeinander folgen.
falls es hilft jeder Eintrag hat eine eindeutige ID

Ich hab das mal mit Lead probiert

CASE
    WHEN BIS = LEAD(VON, 1) OVER (PARTITION BY gruppe,
    name,
    datum
ORDER BY
    gruppe,
    name,
    datum,
    von) /*next id*/
    menge + LEAD(MENGE, 1) OVER (PARTITION BY gruppe,
    name,
    datum
ORDER BY
    gruppe,
    name,
    datum,
    von)


----------



## Zvoni (12. März 2020)

hmm, hätte jetzt eher was in der Form
MIN(VON)
MAX(BIS)
SUM(Menge) ..... WHERE Bis=LEAD(Von,1) OVER (blablablabla) GROUP BY Gruppe, Name, Datum erwartet.

EDIT: Kenn mich in Oracle nicht aus. Deshalb weiss ich jetzt nicht ob mein Vergleich ins WHERE oder in eine HAVING muss/soll

EDIT: Bin mir nicht sicher, aber müsste das nicht mit einem INNER JOIN auf sich selbst gehen?

AIRCODE!


```
SELECT T1.Gruppe, T1.Name, T1.Datum, Min(T1.Von), Max(T1.Bis), SUM(T1.Menge)
FROM Tabelle T1
INNER JOIN
Tabelle T2
ON
T1.Gruppe=T2.Gruppe AND
T1.Name=T2.Name AND
T1.Datum=T2.Datum AND
T1.Bis=T2.von
WHERE
Irgendwas
GROUP BY
T1.Gruppe,
T1.Name,
T1.Datum
```


----------



## warface (12. März 2020)

Zvoni, danke für deine Unterstützung.
Leider hat's damit nicht geklappt.
Ich habe aber inzwischen selber was gebaut, werde es morgen mal hier einfügen.
vielleicht habt Ihr noch Verbesserungen ;-)

Ich hab mit LAG und LEAD den ersten und den letzten Datensatz der zusammenhängenden Zeilen markiert und dass als Basis genutzt.


----------



## Yaslaw (16. März 2020)

@warface 
Funktioniert deine Lösung? Wie sieht sie aus?


----------



## warface (16. März 2020)

so siehts bei mir aus,
Verbesserungen sind erwünscht 

*EDIT*
hatte einen kleinen Denkfehler, nun funktionierts ;-)


```
SELECT

    Gruppe,
    Name,
    Datum,
    von,
    bisX as bis,
    menge,
    /*Wenn das Ende-Kennzeichen = 'X' ist, wird die Menge der Datebsätze zwischen Von und BisX summiert*/
    CASE
        WHEN ende = 'X' THEN Menge
        ELSE (
        SELECT
            SUM(Menge)
        FROM
            Daten
        WHERE
            Gruppe = x.Gruppe
            AND Name = x.Name
            AND Datum = x.Datum
            AND von >= x.von
            AND bis <= x.bisX)
END AS Menge,
    Anfang,
    Ende
FROM
    (
    /*
    Wenn das Ende-Kennzeichen = 'X' ist wird Bis verwendet, ansonsten bis des Nachfolgers
    */
    SELECT
            Gruppe,
            Name,
            Datum,
            von,
            bis,
            menge,
            CASE
            WHEN ende = 'X' THEN bis
            ELSE LEAD(bis, 1) OVER (PARTITION BY Gruppe,Name,Datum ORDER BY Gruppe,Name,Datum)
            END AS bisX,
        Menge,
        Anfang,
        Ende
    FROM
(
    /*Es werden nur noch Anfang- und End-Buchungen ausgegeben */
    Select * from
        (
        /*
        Es wird ein Kennzeichen beim ersten und letzten Datensatz einer zusammenhängenden Kette gesetzt
        Wenn kein direkter Nachfolger(BIS=VON des Nachfolgers) exisitert wird 'X' in der Ende-Spalte gesetzt
        Wenn kein direkter Vorgänger(VON=BIS des Vorgängers) existiert wird 'X' in der Anfang-Spalte gesetzt
        */
        SELECT
            Gruppe,
            Name,
            Datum,
            von,
            bis,
            menge,
        CASE
                WHEN LAG(bis, 1) OVER (PARTITION BY Gruppe,Name,Datum
            ORDER BY Gruppe,Name,Datum) = von THEN ''
                ELSE 'X'
        END AS Anfang,
        CASE
                WHEN LEAD(von, 1) OVER (PARTITION BY Gruppe,Name,Datum
            ORDER BY Gruppe,Name,Datum) = bis THEN ''
                ELSE 'X'
        END AS Ende
        FROM
            DATEN
        ORDER BY
            1,
            2,
            3,
            4) where Anfang = 'X' or Ende = 'X') x
```


----------

