SELECT mit JOIN und LIMIT GROUP BY Problem
fr@gma
- php
Hallo!
Ich habe ein Problem mit meinem Select-Statement.
Ich habe drei Tabellen:
kundendaten
anrede, name, kundennummer, id ...
rechnungen
rechnungnummer, kundennummer, id ...
positionen
rechnungnummer, anzahl, preis ...
Nun möchte ich immer 5 Rechnungen auf einmal anzeigen:
$alledaten_result = mysql_query("SELECT
rechnungen.kundennummer AS rekunr,
rechnungen.rechnungsnummer AS renr,
rechnungen.rechnungid AS reid,
kundendaten.anrede AS kuan,
kundendaten.name AS kuna,
kundendaten.kundennummer AS kunr,
positionen.rechnungsnummer AS ponr,
positionen.anzahl * positionen.preis AS gesamt
FROM rechnungen
LEFT JOIN kundendaten
ON rechnungen.kundennummer = kundendaten.kundennummer
LEFT JOIN positionen
ON rechnungen.rechnungsnummer = positionen.rechnungsnummer
GROUP BY gesamt
ORDER BY rechnungen.rechnungsnummer
LIMIT $anzeige, 5");
Nun bekomme ich zwar die 5 Rechnungen angezeigt, Probleme bereitet mir aber das eine Rechnung mehrere Positionen beinhalten kann.
Der Gesamtpreis aus anzahl und preis wird zwar berechnet aber nur für die erste Position.
Ich habe keine Idee wie ich das Ergebnis aller Positionen ermitteln kann!
Könnte mir jemand einen Tipp geben?
Danke im voraus!!!
LG fr@gma
Hallo,
Nun möchte ich immer 5 Rechnungen auf einmal anzeigen:
SELECT
rechnungen.kundennummer AS rekunr,
rechnungen.rechnungsnummer AS renr,
rechnungen.rechnungid AS reid,kundendaten.anrede AS kuan,
kundendaten.name AS kuna,
kundendaten.kundennummer AS kunr,positionen.rechnungsnummer AS ponr,
positionen.anzahl * positionen.preis AS gesamtFROM rechnungen
LEFT JOIN kundendaten
ON rechnungen.kundennummer = kundendaten.kundennummer
LEFT JOIN positionen
ON rechnungen.rechnungsnummer = positionen.rechnungsnummer
GROUP BY gesamt
ORDER BY rechnungen.rechnungsnummer
LIMIT $anzeige, 5
was für ein Glück, das nur MySQL ein derart fehlerhaftes Statement ausführt.
Was möchtest Du nun genau haben?
Jeweils die zusammengefaßten Daten von fünf Rechnungen?
Kundennummer,
Rechnungsnummer
Rechnungsid (wozu diese, wenn es eine Rechnungsnummer gibt?)
Werden Rechnungsnummern wiederverwendet? Wenn ja, darfst Du die Positionen auch nur über die rechnungid zuordnen.
Anrede
Name
und nochmals die Kundennummer (läßt man weg)
und nochmals die Rechnungsnummer (läßt man ebenfalls weg)
tja und dann den Rechnungsbetrag dieser einen Rechnung.
1. Schritt:
Berechne den Rechnungsbetrag jeder Rechnung (dazu benötigst Du die Aggregatsfunktion SUM(). Du benötigst nur die Tabelle positionen.
Gruppiere nach Rechnungsnummer.
2. Schritt:
Lass Dir die Rechnungsdetails anzeigen. Da die Zuordnung eindeutig sein sollte, und alle anderen Spalten der Tabelle Rechnung eindeutig von der Rechnungsnummer abhängen, könntest und solltest Du nach dem Join problemlos nach allen Spalten außer dem Rechnungsbetrag gruppieren.
3. Schritt
Die Kundendaten sollten ebenfalls eindeutig von der Rechnungsnummer abhängen, so dass Du problemlos die Kundendaten dazujoinen kannst und nach *allen* Spalten außer dem Rechnungsbetrag gruppieren kannst.
Ist Dir dies zuviel des Gruppierens, so joine die Rechnungstabelle mit der 1. Abfrage (Berechnung des Rechnungsbetrages) als temporärer View und anschließend die Kundendaten dazu. Durch den temporären View entfällt das weitere Gruppieren.
Du hast nach der einzigen Spalte gruppiert, nach der Du *nicht* gruppieren, sondern auf die Du eine Aggregatsfunktion anwenden solltest.
Ungetesteter Code für den Einsatz des temporären Views
1. Schritt:
Berechne den Rechnungsbetrag je Rechnung (Rechnungsnummer):
SELECT -- Gib mir
p.rechnungsnummer -- die Rechnungsnummer
SUM(p.anzahl * p.betrag) rechnungsbetrag -- und den Rechnungsbetrag,
-- der sich aus der Summe der Positionsbeträge
-- berechnet, die sich wiederum aus dem Produkt
-- von Einzelpreis und Anzahl berechnen
FROM -- aus der Tabelle
positionen p -- Positionen, die mit dem Alias p angesprochen wird
-- auf das optionale AS verzichten wir aus
-- Kompatibilitätsgründen :-)
GROUP BY -- gruppiert nach
p.rechnungsnummer -- den Rechnungsnummern, d.h. ein Eintrag je
-- Rechnungsnummer
Schritt 2:
Wir nutzen die Abfrage aus Schritt 1 als temporären View und joinen diese Abfrage mit der Rechnungstabelle:
SELECT -- Gib mir
r.rechnungsnummer, -- die Rechnungsnummer
r.kundennummer, -- die zugehörige Kundennummer
r.rechnungid, -- die Rechnungsid
p.rechnungsbetrag -- und den Rechnungsbetrag
FROM -- der
rp.rechnungen r -- Rechnungen, die wir bequemerweise über r
-- ansprechen. Verzicht auf AS siehe oben.
INNER JOIN ( -- die mit den Rechnungspositionen
SELECT -- aus Schritt 1
p.rechnungsnummer,
SUM(p.anzahl * p.preis) rechnungsbetrag
FROM
positionen p
GROUP BY
p.rechnungsnummer
) rp -- (der temporäre View benötigt zwingend einen
-- Aliasnamen (rp für rechnungspositionen))
ON -- über die
r.rechnungsnummer = rp.rechnungsnummer -- Rechnungsnummer verknüpft ist.
Die Vervollständigung mit den Kundendaten überlasse ich Dir als Übung.
Anmerkung: Vollständige Tabellennamen als Aliasnamen zu verwenden bzw. zur genauen Spaltenansprache, ist in den wenigsten Fällen sinnvoll und trägt meist nicht zur Lesbarkeit einer Abfrage bei.
Bei SQL-Problemen ist es nicht sinnvoll, PHP-Code zu posten, der SQL-Code erzeugt. Zuerst sollte man händisch das gewünschte SQL-Statement schreiben, bevor man versucht, es mit $programmiersprache zusammenzubauen. Es ist natürlich nicht notwendig, jeden benötigten LIMIT-Wert von Hand auszutesten :-)
Freundliche Grüße
Vinzenz
Hallo Vinzenz!
Vielen Dank für Dein Posting!
Für meine Anfrage im Forum hatte ich mein Statement etwas umgeschrieben, da ich es stark kürzen müsste, sorry!
- Schritt:
Berechne den Rechnungsbetrag je Rechnung (Rechnungsnummer):
> SELECT
> p.rechnungsnummer
> SUM(p.anzahl * p.betrag) rechnungsbetrag
> FROM
> positionen p
> GROUP BY
> p.rechnungsnummer
>
Schritt 2:
Wir nutzen die Abfrage aus Schritt 1 als temporären View und joinen diese Abfrage mit der Rechnungstabelle:
> SELECT
> r.rechnungsnummer,
> r.kundennummer,
> r.rechnungid,
> p.rechnungsbetrag
> FROM
> rp.rechnungen r
> INNER JOIN (
> SELECT
> p.rechnungsnummer,
> SUM(p.anzahl * p.preis) rechnungsbetrag
Ist hier gemeint, die Gesamtsumme aller Positionen einer Rechnung zu berechnen, oder?
Sonst versteh ich die Notwendigkeit von Schritt 1 nicht so richtig.
Mit "JOIN" kenn ich mich nicht so aus, da muss ich noch bisschen proben!
> FROM
> positionen p
> GROUP BY
> p.rechnungsnummer
> ) rp
> ON
> r.rechnungsnummer = rp.rechnungsnummer
ist.
Werd das gleich mal einbauen und probieren, vielen Dank nochmal
LG fr@gma
Hab schon gemerkt, war völliger Unsinn was ich im letzten Beitrag geschrieben hatte.
Hab das jetzt so gelöst:
$alledaten_result = mysql_query("SELECT
r.kundennummer,
r.rechnungsdatum,
r.rechnungsnummer,
k.anrede,
k.vorname,
k.name,
k.kundennummer,
p.rechnungsnummer,
SUM(p.anzahl * p.einzelpreis)
FROM rechnungen r
LEFT JOIN kundendaten k
ON r.kundennummer = k.kundennummer
LEFT JOIN positionen p
ON r.rechnungsnummer = p.rechnungsnummer
GROUP BY p.rechnungsnummer
ORDER BY r.rechnungsnummer
LIMIT $anzeige, 14");
... und geht.
Ich finde das so übersichtlicher.
Ob das Nachteile hat, muss ich natürlich noch ausprobieren.
Naja, jeder fängt mal an!!!
Vielen Dank nochmal an Vinzenz, Dein Beitrag hat mir gut helfen können, vor allem mit den Tabellenbezeichnungen statt den "AS".
LG fr@gma
Hello,
SELECT
r.kundennummer,
r.rechnungsdatum,
r.rechnungsnummer,
k.anrede,
k.vorname,
k.name,
k.kundennummer,
p.rechnungsnummer,
SUM(p.anzahl * p.einzelpreis)
GROUP BY p.rechnungsnummer>>
... und geht.
ahhhhhhhhh - ich verfluche MySQL. Was du da baust ist laut SQL-Standard ein falsches Statement. Du darfst in einer Gruppierung nur Spalten selektieren nach denen entweder gruppiert wurde (p.rechnungsnummer), oder die auf einer Aggregatsfunktion (MAX, MIN, SUM, ...) basieren (SUM(p.anzahl * p.einzelpreis)). Für alle anderen Werte liegt nämlich praktisch keine Vorschrift vor, welcher Wert aus der Gruppe genommen werden soll. Wenn sie für die Gruppe alle gleich sind, dann nimm sie in das GROUP BY mit auf, wenn nicht, dann überlege dir bitte unter welchen Gesichtspunkten man den richtigen Wert auswählt.
Nochmal: MySQL akzeptiert dein Statement und reimt sich zusammen was du gemeint haben könntest, aber es ist schlichtweg fehlerhaft...
MfG
Rouven
Hallo Rouven
danke für Dein Posting.
SELECT
r.kundennummer,
r.rechnungsdatum,
r.rechnungsnummer,
k.anrede,
k.vorname,
k.name,
k.kundennummer,
p.rechnungsnummer,
SUM(p.anzahl * p.einzelpreis)
GROUP BY p.rechnungsnummer>>
... und geht.
ahhhhhhhhh - ich verfluche MySQL. Was du da baust ist laut SQL-Standard ein falsches Statement. Du darfst in einer Gruppierung nur Spalten selektieren nach denen entweder gruppiert wurde (p.rechnungsnummer),
Leider versteh ich das nicht.
oder die auf einer Aggregatsfunktion (MAX, MIN, SUM, ...) basieren (SUM(p.anzahl * p.einzelpreis)). Für alle anderen Werte liegt nämlich praktisch keine Vorschrift vor, welcher Wert aus der Gruppe genommen werden soll.
Die Tabelle positionen sieht beispielhaft so aus:
rechnungsnummer anzahl einzelpreis id
----------------+--------+-------------+------
1005 2 12 0
1005 1 30 1
1005 1 112 2
1006 1 10 3
usw.
Ich möchte ja die Werte anzahl multiplizieren mit preis
SUM(p.anzahl * p.einzelpreis)
Damit ich die Ausgabe von Rechnungen limitieren kann, muss ich ja die Werte z.B. von 1005 miteinander gruppieren.
Vielleicht habe ich ja hier einen Denkfehler, für mich ist gruppieren gleich bedeutend mit summieren, oder?
Da ich das Produkt von anzahl und preis ja ermittelt habe, bleibt doch eigentlich nur die Summe des Produktes, oder muss ich formulieren:
SUM(p.anzahl * p.einzelpreis) betrag
und im select extra nochmal
p.betrag
mit hinein nehemn?
Wenn sie für die Gruppe alle gleich sind, dann nimm sie in das GROUP BY mit auf, wenn nicht, dann überlege dir bitte unter welchen Gesichtspunkten man den richtigen Wert auswählt.
Nochmal: MySQL akzeptiert dein Statement und reimt sich zusammen was du gemeint haben könntest, aber es ist schlichtweg fehlerhaft...
LG fr@gma
Hello,
Leider versteh ich das nicht.
dann lass mich dir ein Beispiel bauen:
Tabelle
a | b | c
x | y | 1
x | z | 2
m | n | 3
m | o | 4
Erläutere mir das Ergebnis von:
SELECT a, b, c FROM tabelle GROUP BY a
Folgende Abfrage ist legal:
SELECT a, SUM(c) FROM tabelle GROUP BY a
Warum legal? Nach a wird gruppiert, für c haben wir eine Bildungsvorschrift angegeben, die auf alle Elemente der Gruppe angewendet werden kann (soll heißen: die Zeilen als solche spielen keine Rolle). Das Ergebnis
x | 3
m | 7
Folgende ist ebenfalls legal
SELECT a, MIN(c) FROM tabelle GROUP BY a
Wiederum haben wir eine Bildungsvorschrift, wir sagen ihm, welches der Elemente pro Gruppe (für x 1 oder 2, für m 3 oder 4) wir verwenden wollen, nämlich das Kleinste:
x | 1
m | 3
Die Spalte y kriegen wir nicht ohne weiteres mit hinein. Um in Bezug auf die selektierten Spalten legales SQL zu schreiben bleiben z.B. folgende Möglichkeiten:
mit gruppieren
SELECT a, b, SUM(c) FROM tabelle GROUP BY a, b
-> liefert allerdings nicht das gewünschte Ergebnis. Wir gruppieren jetzt plötzlich Zeilen, die bei a UND b den selben Wert haben - Ergebnis:
x | y | 1
x | z | 2
m | n | 3
m | o | 4
Aggregationsfunktion
Analog obigem SUM/MIN/... für die Zeichendaten, bietet MySQL z.B. die Möglichkeit alle Strings aneinanderzufügen:
SELECT a, GROUP_CONCAT(b), SUM(c) FROM tabelle GROUP BY a
Ergebnis:
x | y,z | 3
m | n,o | 7
Daher nochmal mein Ausgangspunkt: du darfst nur Spalten selektieren, bei denen eindeutig geklärt ist, wie der Wert aussieht. Für deine Rechnungen, sofern der Datenstand konsistent ist, gebe ich folgende Abwandlung von oben zu bedenken:
Tabelle
a | b | c
x | y | 1
x | y | 2
m | n | 3
m | n | 4
SELECT a, b, SUM(c) FROM tabelle GROUP BY a, b
Da hier die Werte für alle Mitglieder der Gruppen x und m identisch ist, lautet das Ergebnis:
x | y | 3
m | n | 7
MfG
Rouven
Hallo Rouven,
vielen Dank für Deine umfangreiche und sehr gut verständliche Erklärung.
In meinem Beispiel bilde ich ja die zeilenweise Summe aus anzahl und einzelpreis.
Deine Angaben habe ich jetzt so verstanden, das SQL in meinem Statement nicht weiß, dass das Ergebnis von (anzahl*einzelpreis) zusammen zufügen ist, sondern das es auch passieren kann das die Spalten einzeln zusammen gefasst werden, indem anzahl summiert wird und einzelpreis summiert wird, oder?
Jedoch habe ich noch immer keine Ahnung wie man das im Statement ausdrücken kann.
Logisch wäre für mich demnach so ein Ausdruck:
SELECT
...
SUM(p.anzahl * p.einzelpreis) betrag
p.betrag
GROUP BY p.betrag
Das habe ich auch versucht, jedoch entstehen da Syntaxfehler.
Mir fehlt einfach der Zeiger zu betrag!
Ich bilde mir aber ein gelesen zu haben, das MYSQL eine virtuelle Tabelle aus dem Statement bildet und wenn vor GROUP BY ein SUM() ermittelt wird, besteht betrag zumindest virtuell und somit funktioniert das Ergebnis letztendlich doch.
Wie könnte man das denn in meinem Beispiel besser lösen?
Auf jeden Fall nochmals ein riesen Dankeschön!!!
LG fr@gma
Hello,
Deine Angaben habe ich jetzt so verstanden, das SQL in meinem Statement nicht weiß, dass das Ergebnis von (anzahl*einzelpreis) zusammen zufügen ist, sondern das es auch passieren kann das die Spalten einzeln zusammen gefasst werden, indem anzahl summiert wird und einzelpreis summiert wird, oder?
nein, das habe ich damit nicht gemeint. Deine Summierung ist vollkommen in Ordnung. Du gruppierst und bildest eine Summe über die Zeilen innerhalb der Gruppe, das ist vollkommen in Ordnung - soweit ich das überblicken kann, dürfte es auch das ergeben, was du haben wolltest.
In meinem Beispiel bilde ich ja die zeilenweise Summe aus anzahl und einzelpreis.
eben, das ist soweit okay.
Dein Problem liegt in den anderen Spalten, ich zitiere nochmal von weiter oben:
SELECT
r.kundennummer,
r.rechnungsdatum,
r.rechnungsnummer,
k.anrede,
k.vorname,
k.name,
k.kundennummer,
p.rechnungsnummer,
SUM(p.anzahl * p.einzelpreis)
[...]
GROUP BY p.rechnungsnummer
Schauen wir uns das nochmal unter der Beschreibung meines vorherigen Postings an:
Tabelle k
4711 | Max | Mustermann
Tabelle p
1234 | Artikel_1 | 13,95 | 1
1234 | Artikel_2 | 14,95 | 1
Wenn ich jetzt den Join ausführe, dann KANN ja nur rauskommen
4711 | 1234 | Max | Mustermann | Artikel_1 | 13,95 | 1
4711 | 1234 | Max | Mustermann | Artikel_2 | 14,95 | 1
Klar sagst du "ist ja kein Problem, steht ja überall Max, steht ja überall Mustermann" - nicht so für SQL. Für SQL gibt es keine sinnvolle Befüllung der Tabelle, genauso gut könnte das Ergebnis deines Joins das hier sein (wohlgemerkt: für SQL, nicht im wahren Leben):
4711 | 1234 | Max | Mustermann | Artikel_1 | 13,95 | 1
4711 | 1234 | John| Doe | Artikel_2 | 14,95 | 1
Was ist jetzt deiner Meinung nach das Ergebnis von
SELECT p.rechnungsnummer, k.vorname, k.nachname, SUM(preis * menge)
FROM ... GROUP BY p.rechnungsnummer?
Ist es vielleicht
1234 | Max | Mustermann | 28,90
oder ist es
1234 | John| Doe | 28,90
oder ist es
1234 | Max | Doe | 28,90
oder
...
MfG
Rouven
Hello,
Deine Angaben habe ich jetzt so verstanden, das SQL in meinem Statement nicht weiß, dass das Ergebnis von (anzahl*einzelpreis) zusammen zufügen ist, sondern das es auch passieren kann das die Spalten einzeln zusammen gefasst werden, indem anzahl summiert wird und einzelpreis summiert wird, oder?
nein, das habe ich damit nicht gemeint. Deine Summierung ist vollkommen in Ordnung. Du gruppierst und bildest eine Summe über die Zeilen innerhalb der Gruppe, das ist vollkommen in Ordnung - soweit ich das überblicken kann, dürfte es auch das ergeben, was du haben wolltest.
In meinem Beispiel bilde ich ja die zeilenweise Summe aus anzahl und einzelpreis.
eben, das ist soweit okay.Dein Problem liegt in den anderen Spalten, ich zitiere nochmal von weiter oben:
SELECT
r.kundennummer,
r.rechnungsdatum,
r.rechnungsnummer,
k.anrede,
k.vorname,
k.name,
k.kundennummer,
p.rechnungsnummer,
SUM(p.anzahl * p.einzelpreis)
[...]
GROUP BY p.rechnungsnummer
Schauen wir uns das nochmal unter der Beschreibung meines vorherigen Postings an:
- du gruppierst nach p.rechnungsnummer. Es ist also legitim, p.rechnungsnummer zu selektieren - GROUP BY stellt sicher, dass es einen eindeutigen Wert hat.
- SUM(p.anzahl * p.einzelpreis) ist legitim, innerhalb der rechnungsnummer wird also für die Posten die Summer über Anzahl * Preis gebildet, hier ist auch kein Zweifel
- alle anderen Spalten sind nicht eindeutig. Wenn du den JOIN mal ohne GROUP BY laufen lässt siehst du ja, dass zeitweilig mehr als eine Zeile pro Rechnungsnummer existiert. Aus Sicht von SQL ist also NICHT sichergestellt, dass die Kundennummer in allen Zeilen die selbe ist. Es wird also von dir verlangt, entweder dafür zu sorgen, dass es eine saubere Gruppierung gibt (also r.kundennummer mit in GROUP BY aufzunehmen), oder eine Bildungsvorschrift anzugeben, welche der kundennummern verwendet werden sollen.
Achso, es muss also für alle selektierten Felder eine Bildungsvorschrift oder Gruppe existieren, oder?
Oder nur für die Tabelle in welcher gruppiert wird?
Stehe jetzt auf dem Schlauch!!!
Also ich habe den Artikel Einführung in Joins gelesen und fasse das so auf, dass durch den Join ja quasi nur noch eine (virtuelle) Tabelle besteht. Da müsste in meinem Fall ja nach allen selektierten Spalten außer anzahl und preis gruppiert werden.
Also wenn das so wäre hätte ich es glaub ich verstanden, da nun für mich wieder eine Logik einkehrt.
Für dich als Mensch ist klar
Tabelle r
4711 | 1234Tabelle k
4711 | Max | MustermannTabelle p
1234 | Artikel_1 | 13,95 | 1
1234 | Artikel_2 | 14,95 | 1Wenn ich jetzt den Join ausführe, dann KANN ja nur rauskommen
4711 | 1234 | Max | Mustermann | Artikel_1 | 13,95 | 1
4711 | 1234 | Max | Mustermann | Artikel_2 | 14,95 | 1Klar sagst du "ist ja kein Problem, steht ja überall Max, steht ja überall Mustermann" - nicht so für SQL. Für SQL gibt es keine sinnvolle Befüllung der Tabelle, genauso gut könnte das Ergebnis deines Joins das hier sein (wohlgemerkt: für SQL, nicht im wahren Leben):
4711 | 1234 | Max | Mustermann | Artikel_1 | 13,95 | 1
4711 | 1234 | John| Doe | Artikel_2 | 14,95 | 1Was ist jetzt deiner Meinung nach das Ergebnis von
SELECT p.rechnungsnummer, k.vorname, k.nachname, SUM(preis * menge)
FROM ... GROUP BY p.rechnungsnummer?
Ist es vielleicht
1234 | Max | Mustermann | 28,90
oder ist es
1234 | John| Doe | 28,90
oder ist es
1234 | Max | Doe | 28,90
oder
...
LG fr@gma
Hello,
Achso, es muss also für alle selektierten Felder eine Bildungsvorschrift oder Gruppe existieren, oder?
genau.
Da müsste in meinem Fall ja nach allen selektierten Spalten außer anzahl und preis gruppiert werden.
genau
MfG
Rouven
Hello Rouven,
ok, da glaube ich – hab ich das jetzt mitgeschnitten.
Nochmals recht vielen Dank für Deine umfangreiche Unterstützung!!!
LG fr@gma
Hallo,
Ist hier gemeint, die Gesamtsumme aller Positionen einer Rechnung zu berechnen, oder?
Ja, die willst Du ja haben.
Sonst versteh ich die Notwendigkeit von Schritt 1 nicht so richtig.
Es ist bei solchen Fragen *immer* eine gute Idee, Beispieldaten anzugeben und das gewünschte Ergebnis - mit der Begründung, warum dies das gewünschte Ergebnis ist.
Mit "JOIN" kenn ich mich nicht so aus, da muss ich noch bisschen proben!
Dazu empfehle ich Dir unsere zwei Join-Artikel in SELFHTML aktuell:
- Einführung in Joins
- Fortgeschrittene Jointechniken
Freundliche Grüße
Vinzenz