SQL falsche Ergebnisse beim Gruppieren

SFW_SQL

Neuer Benutzer
Beiträge
3
Hallo zusammen,

ich bin recht neu beim Thema SQL und habe ein Problem beim gruppieren.
Folgender SQL funktioniert wie gewünscht:

Select sum(rt1.erlöse)
From Vertrag v
Inner Join Rechnung1 r1 on v.id1 = r1.id1
Inner Join Rechnungsteil rt1 on r1.id2 = rt1.id2
Group by v.nummer

Nun möchte ich mir aus einer weiteren Rechnungstabelle ebenfalls die Summe der Erlöse je Vertrag anzeigen lassen. Wenn ich aber folgendes hinzufügen:
Inner Join Rechnung2 r2 on v.id3 = r2.id3
Dann ist die Summe von rt1.erlöse um ein vielfacher höher als vorher (und zwar um die Anzahl der Datensätze aus Tabelle r2).
Wieso ist das so und wie kann ich das verhindern?

Besten Dank vorab für eure Hilfe!
VG
 
Zuletzt bearbeitet:
Werbung:
Nun bei einem Join verknüpfst du Datensätze aus Tabelle A und B über ein Kriterium. Wenn das Kriterium in einer der Tabellen auf mehr als einen Datensatz zutrifft dann multipliziert sich die Anzahl der Datensätze im Ergebnis. Das gleiche gilt für bereits gejointe Tabellen, das Ergebnis multipliziert sich erneut.

Du kannst das natürlich umgehen. Am einfachsten dürfte es sein erst eine Summe zu ermitteln, dann die andere, und dann die beiden Ausgaben zu joinen.

Also mal ungefähr:
Code:
SELECT t1.nummer,t1.erlöse,t2.nummer,t2.erlöse
FROM (
Select v.nummer,sum(rt1.erlöse) AS erlöse
From Vertrag v
Inner Join Rechnung1 r1 on v.id1 = r1.id1
Inner Join Rechnungsteil rt1 on r1.id2 = rt1.id2
Group by v.nummer
) t1
FULL OUTER JOIN (
Select v.nummer,sum(rt2.erlöse) AS erlöse
From Vertrag v
Inner Join Rechnung2 r2 on v.id2 = r2.id2
Inner Join Rechnungsteil rt2 on r2.id2 = rt2.id2
Group by v.nummer
) t2
ON t1.nummer = t2.nummer
Das kann man natürlich noch anders formulieren oder kürzen. Aber deine Tabellennamen und Spaltennamen werfen erstmal Fragen auf. Warum gibt es verschiedene Tabellen für die Entität Rechnung? Auch eine Spalte id2 ist sonderbar. Was passiert bei einer dritten Rechnung? Im Ergebnis sieht es so aus als würdest du lediglich mehrere Rechnungspositionen zu einer Rechnungssumme zusammen führen und mehrere Rechnungen pro Vertrag nicht in mehreren Zeilen (Vertragsnummer,Rechnungsdatum,Rechnungsbetrag und davon zwei Zeilen) haben wollen sondern das Ergebnis davon als Pivot darstellen wollen.
 
Inner Join Rechnung2 r2 on v.id3 = r2.id3
Dann ist die Summe von rt1.erlöse um ein vielfacher höher als vorher (und zwar um die Anzahl der Datensätze aus Tabelle r2).
Du kannst ganz einfach nachsehen, was da passiert. Entferne die Gruppierung und lasse Dir von jeder gejointen Tabelle die Daten ausgeben, also wenigstens wichtige "Wiedererkennungsmerkmale" der Datensätze und natürlich die Felder, die Du eigentlich summieren willst.
Es ist ziemlich sicher, dass durch deinen Join die Werte mehrfach vorkommen. Indikator dafür ist der primäre Schlüssel eines Joindatensatzes. Auch wenn alle zu summierenden Felder gleich sind oder sehr ähnlich und für sich genommen kaum ihre Herkunft verraten. Der PK tut das.
@ukulele hat schon rein Rezept geliefert, wie das Problem zu lösen ist.
 
Erstmal vielen Dank für die Antwort und die Mühe! Beide Ausgaben zu joinen ist interessant. Ich gebe zu, dass meine Formulierung nicht präzise genug war. Deswegen nochmal ausführlicher:
Ich habe die Tabelle "Vertrag", welche über die id1 mit einer Tabelle für Ausgangsrechnungen (Rechnung1) verknüpft ist. In dieser Tabelle gibt es mehrere Datensätze pro Vertrag. Jede dieser Rechnungen ist wiederum über eine RechnungsID (id2) mit der Tabelle Rechnungsteil1 verknüpft. In dieser Tabelle befinden sich alle Rechnungsteile zu den Rechnungen und die Spalte "Erlöse", wo pro je Rechnungsteil ein Betrag eingetragen ist.
Den gleichen Tabellenaufbau gibt es nun auch noch für die Eingangsrechnungen zu den Verträgen. Ausgehend von Tabelle "Vertrag" ist jeder Vertrag über die id3 mit der Tabelle für Eingangsrechnungen (Rechnung2) verknüpft. Auch hier gibt wieder mehrere Rechnungen pro Vertrag und eine Verknüpfung zu der Tabelle mit den Rechnungsteilen (Rechnungsteil2) über die id4. Simultan zu den Ausgangsrechnungen gibt auch hier wieder die Erlöse (Spalte "Erlöse2") je Rechnungsteil.
Als Ergebnis des SQL´s möchte ich nun eine Ausgabezeile je Vertrag. Je Vertrag möchte ich dann die Summe aller Erlöse der Ausgangsrechnungen sehen und in einer weiteren Spalte die Summe aller Erlöse der Eingangsrechnungen.

Eine Frage noch: Wofür steht t1 und t2 in deinem Code?

VG
 
t1 und t2 sind Tabellen Aliase, sie sind ein Alias für die zuvor genannte Tabelle, hier ist es ein Select Ausdruck in Klammern, innerhalb dieses Selects gibt es das Gleiche in Grün: ...vertrag v.... v ist ein Alias für die Tabelle vertrag.
Es ist immer sinnvoll mit Table Alias zu arbeiten, manchmal zwingend. Und wenn man es tut, kommt man schnell zu dem Punkt, dass es auch bei Spalten gut oder unumgänglich ist > Spaltenalias.
Du kannst t1 und t2 auch Kasper und Seppl nennen, technisch ganz egal. Ideal ist eine sprechende Abkürzung.

Was Du nicht fragst:
Du hast Dir viel Mühe(Worte) gegeben, die Zusammenhänge genauer zu beschreiben. Das ist nett, aber nicht unbedingt hilfreich für präzise Empfehlungen. Am einfachsten, Du postest das Table Create Statement jeder beteiligten Tabelle inkl. der Constraints. Dort steht alles drin, was man wissen muss. Das nennt sich Datenmodell bzw. hier wäre es dann ein Teil davon.
Anhand dieser Angaben kann man das SQL Statement richtig zusammenstellen, ohne dass es Missverständnisse mit umgangssprachlichen Beschreibungen gibt.
 
Och die Beschreibung ist schon ganz gut verständlich. Vermutlich ist das Design so vorgegeben und du entwickelst das nicht selber?

Jedenfalls ist klar das die Rechnungen (aus zwei Tabellen; n:1 zum Vertrag) in einem sinnvollen Zusammenhang mit dem Vertrag stehen aber eben nicht jede Ausgangsrechnungen exakt zu einer Eingangsrechnungen, vermutlich gibt es da gar keine Verbindung. Der Join macht aber genau das, er stellt jeder Ausgangsrechnung auch jede Eingangsrechnung gegenüber weil eine Einschränkung fehlt.

Ich würde die Summen gar nicht mit einem GROUP BY im Hauptselect bilden, wie gesagt es gibt viele brauchbare Wege das zu lösen. Nachvollziehbarer ist eventuell etwas wie das hier:
Code:
SELECT v.*,
( SELECT sum(e.betrag)
FROM eingangsrechnungen e
WHERE e.vID = v.ID ) AS summe_eingangsrechnungen,
( SELECT sum(a.betrag)
FROM ausgangsrechnungen a
WHERE a.vID = v.ID ) AS summe_ausgangsrechnungen
FROM verträge v
Das ist jetzt natürlich vereinfacht, du musst im Subselect innerhalb des SELECT-Teils noch joinen um deine Beträge zu ermitteln. Entscheidend ist, das du im Subselect auf ein Kriterium aus dem Hauptselect zugreifen kannst, in deinem Fall v.ID. Das Prinzip ist einfach und der Äußere Select ist völlig frei definierbar mit WHERE-Kriterien etc.

Negativ an dieser Version kann die Performance werden. Jede Zeile von verträge führt zu zwei Selects auf die Rechnungstabellen. Aber von der Grundlogik her erstmal simpel und auch nicht zwingend ein no go.
 
Um dann noch eine andere Lösung vorweg zu nehmen:
Code:
WITH e AS (
SELECT vID,sum(betrag) AS summe_eingangsrechnungen
FROM eingangsrechnungen
GROUP BY vID
), a AS (
SELECT vID,sum(betrag) AS summe_ausgangsrechnungen
FROM ausgangsrechnungen
GROUP BY vID
)
SELECT *
FROM verträge v
LEFT JOIN e
ON v.ID = e.vID
LEFT JOIN a
ON v.ID = a.vID
 
Ja richtig das Design ist mehr oder weniger so vorgegeben und ich bin auch eigentlich fachfremd.
Genau, die beiden Rechnungstabellen sind nicht miteinander verknüpft.
Besten Dank schon mal für die Lösungsansätze. Ich werde diese in Kürze testen.
VG
 
Hallo zusammen,
ich verzweifle fast an dieser Aufgabe. Kann mir jemand sagen, wie ich in Spalte b und c die Zahlen nach oben bekomme anstatt Null?

Ist eine Tabelle und ich habe es mit Group by und sortieren probiert aber leider ohne Erfolg.

Vielen Dank



Unbenannt.PNG
 

Anhänge

  • Unbenanntes Diagramm.drawio.png
    Unbenanntes Diagramm.drawio.png
    51,4 KB · Aufrufe: 1
ich verzweifle fast an dieser Aufgabe. Kann mir jemand sagen, wie ich in Spalte b und c die Zahlen nach oben bekomme anstatt Null?

Ist eine Tabelle und ich habe es mit Group by und sortieren probiert aber leider ohne Erfolg.
Code:
...
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
...
 
Werbung:
Ist eine ziemlich schwachsinnige Tabelle.

NULLS FIRST kennt MSSQL nicht.
Code:
 SELECT * FROM tabelle ORDER BY (CASE WHEN b IS NULL THEN 0 ELSE 1 END),b,(CASE WHEN c IS NULL THEN 0 ELSE 1 END),c
Wenn darüber hinaus noch fragen sind bitte in einem eigenen Thread.
 
Zurück
Oben