MySQL Abfrage um JOIN erweitern
Hallo SQL Experten,
ich bräuchte mal wieder eure Hilfe...
Ich habe folgende Abfrage für eine Statistik
welche soweit auch funktioniert. Jedoch liefert die Zeile
ein falsches Ergebnis.
Besser wäre wenn ich einen JOIN auf die Tabelle aauftrag machen könnte, der mir dort mit COUNT(aanr) die Anzahl der Sendungen für den Tag holt, dessen Status !=3 ist. Der Status 3 besagt das der Auftrag storniert ist. Das Feld in der Tabelle aauftrag zum ermitteln des Datums für den Tag heißt ALadetag. Also im Grunde soll in der Tabelle aauftrag die Anzahl der Sendungen für den jeweiligen Tag ermittelt werden mit den gleichen Kriterien der restlichen Abfrage.
Vielen Dank vorab für eure Hilfe!
ich bräuchte mal wieder eure Hilfe...
Ich habe folgende Abfrage für eine Statistik
SELECT SUM(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Gesamt, 0)) - SUM(IF(status != 3 AND TransAnr = 'Gutschrift' AND TYPE=3 AND RG_NUMMER !='Dienstgut' , Gesamt, 0)) AS umsatz,
SUM(IF(status != 3 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Untern_Verguetung, 0)) AS kosten,
SUM(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Gesamt, 0)) - SUM(IF(status != 3 AND TransAnr = 'Gutschrift' AND TYPE=3 AND RG_NUMMER !='Dienstgut' , Gesamt, 0)) - SUM(IF(status != 3 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Untern_Verguetung, 0)) AS ergebnis,
((SUM(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Gesamt, 0)) - SUM(IF(status != 3 AND TransAnr = 'Gutschrift' AND TYPE=3 AND RG_NUMMER !='Dienstgut' , Gesamt, 0))) - SUM(IF(status != 3 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Untern_Verguetung, 0)))*100 / (SUM(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Gesamt, 0)) - SUM(IF(status != 3 AND TransAnr = 'Gutschrift' AND TYPE=3 AND RG_NUMMER !='Dienstgut' , Gesamt, 0))) AS rendite,
COUNT(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , aanr, 0)) AS sendungen,
(SUM(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Gesamt, 0)) - SUM(IF(status != 3 AND TransAnr = 'Gutschrift' AND TYPE=3 AND RG_NUMMER !='Dienstgut' , Gesamt, 0))) / COUNT(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , aanr, 0)) AS durchschnitt,
(SUM(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Gesamt, 0)) - SUM(IF(status != 3 AND TransAnr = 'Gutschrift' AND TYPE=3 AND RG_NUMMER !='Dienstgut' , Gesamt, 0))) / COUNT(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , aanr, 0)) - (SUM(IF(status != 3 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , Untern_Verguetung, 0)) / COUNT(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , aanr, 0))) AS ergebnisd,
DAYOFMONTH(DATE(DATUM)) AS day
FROM posten
WHERE EXTRACT(YEAR_MONTH FROM DATUM) = '{$yearMonth}'
GROUP BY DATE(DATUM)
welche soweit auch funktioniert. Jedoch liefert die Zeile
COUNT(IF(status !=3 AND status !=4 AND TransAnr != 'Gutschrift' AND RG_NUMMER !='Dienstgut' , aanr, 0)) AS sendungen,
Besser wäre wenn ich einen JOIN auf die Tabelle aauftrag machen könnte, der mir dort mit COUNT(aanr) die Anzahl der Sendungen für den Tag holt, dessen Status !=3 ist. Der Status 3 besagt das der Auftrag storniert ist. Das Feld in der Tabelle aauftrag zum ermitteln des Datums für den Tag heißt ALadetag. Also im Grunde soll in der Tabelle aauftrag die Anzahl der Sendungen für den jeweiligen Tag ermittelt werden mit den gleichen Kriterien der restlichen Abfrage.
Vielen Dank vorab für eure Hilfe!
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 324092
Url: https://administrator.de/contentid/324092
Ausgedruckt am: 15.11.2024 um 23:11 Uhr
14 Kommentare
Neuester Kommentar
Moin,
ganz ehrlich?
Das Teil sieht sehr imperformant aus...
bau die erstmal eine Tabelle/ View, mit der du die Daten mit deinen Standardabfragen einschränkst:
(ich kürze View dann immer mit VI ab, findet man schneller)
Dann hättest du schonmal eine erste verdichtete Tabelle, welche man Joinen kann und beim ersten Aufruf erzeugt wird.
Gleiches dann nochmal, aber ohne deine EInschränkung
Denn die kommt auch häufiger vor....
Mit denen kannst du erstmal dein Statement entschlacken
Für dein finales Statement machst du dann den Join via
So in die Richtung würde es jedenfalls gehen, aber das ist nur Blind geraten, ohne genaueres zu kennen...
Ansonsten wären Tabellenköpfe, und dein erwartetes Ziel ganz interessant. Per Hand eingetragenene Excel-Daten (anonymisiert) reichen erstmal, damit man mal ein Bild hat...
Gruß
em-pie
ganz ehrlich?
Das Teil sieht sehr imperformant aus...
bau die erstmal eine Tabelle/ View, mit der du die Daten mit deinen Standardabfragen einschränkst:
(ich kürze View dann immer mit VI ab, findet man schneller)
Create or replace View "VI_POSTEN1"
select
SUM(Gesamt) as Gesamt
Date(Datum)
from
posten
where
status !=3
AND status !=4
AND TransAnr != 'Gutschrift'
AND RG_NUMMER !='Dienstgut'
GROUP BY
Date(Datum)
Gleiches dann nochmal, aber ohne deine EInschränkung
Create or replace View "VI_POSTEN2"
select
SUM(Gesamt) as Gesamt
Date(Datum)
from
posten
where
status !=3
AND TransAnr != 'Gutschrift'
AND RG_NUMMER !='Dienstgut'
GROUP BY
Date(Datum)
Mit denen kannst du erstmal dein Statement entschlacken
Für dein finales Statement machst du dann den Join via
Select tbl1.Gesamt-tbl2.Gesamt, tbl1.Gesamt-tbl3.Gesamt, ...
From Posten as tbl1
Inner Join VI_Posten1 as tbl2 on tbl1.Datum = tbl2.Datum
Inner Join VI_Posten2 as tbl3 on tbl1.Datum = tbl3.Datum
....
Ansonsten wären Tabellenköpfe, und dein erwartetes Ziel ganz interessant. Per Hand eingetragenene Excel-Daten (anonymisiert) reichen erstmal, damit man mal ein Bild hat...
Gruß
em-pie
Moin datadexx,
ich würde ein paar Sachen anders angehen.
Dann würde sich dein Gestrunkele auf dieses hier reduzieren (ungetestet natürlich)
Auf ein GROUP BY der Tabelle aauftrag nach Tagen habe ich verzichtet - für deine max 31 Tage mache ich dann lieber 31x ein Inline-Select mit einem Count-Alles-was-kein-Storno-ist.
Die Aliasnamen ergeben sich als p=posten, pg=postenGruppiert, pgXSendungen=pg mit Sendungen.
Ein künstliches Feld "DateAusPosten" habe ich noch zum Hochreichen in den SELECTs zugefügt.
Ach ja, und dort wo dividiert wird bei den Mittelwertberechungen habe ich noch das DIV/0 abgefangen.
Grüße
Biber
[Edit] In Zeile 13 den Nenner (zwsum1-zwsum2) in Klammern gesetzt... wegen Punkt-vor-Strich-Rechnung [/Edit]
ich würde ein paar Sachen anders angehen.
- BEVOR du mit irgendwelchem GROUP BY und Rum-Aggregieren anfängst, weisst du doch schon, dass du aus der "posten"-Tabelle nur die Datensätze brauchst, die deinem Parameter '{$yearMonth}' entsprechen und wo RG_Nummer != 'Dienstgut' ist.Dann hol doch nur die aus "Posten" mit einem WHERE
- Skeptisch macht mich das "GROUP by DATE(Datum)" - ist das denn ein TEXT-Feld oder warum willst du das nochmal casten?
- ein JOIN auf die aauftrag-Tabelle ist keine tolle Idee - du müsstest erst "posten" wie "aauftrag" einzeln nach Datum gruppieren und dann über Datum verjoinen. Die Detail-Tabellen über Datum verknüpfen würde ein unsinniges Ergebnis beim JOIN bringen (ein JOIN von 12 Posten am letzten Dienstag auf 3 Auftrags-Sendungen am letzten Dienstag sind wieviel Sätze?)
- unterm Strich brauchst du aus der "aggregierten Posten-Tabelle" nur 2 Zwischensummen zur Berechnung vom "umsatz" und die "Kosten", alles andere ist abgeleitet. und aus "aauftrag" brauchst du nur die "Anzahl Sendungen".
Dann würde sich dein Gestrunkele auf dieses hier reduzieren (ungetestet natürlich)
SELECT umsatz
, kosten
, Ergebnis
, rendite
, sendungen,
, IF(sendungen != 0, umsatz/sendungen, 0) as Durchschnitt
, IF(sendungen != 0 , ergebnis/sendungen, 0) as Ergebnisd
, DAYOFMONTH(dateausposten) AS day
FROM (
Select zwsum1-zwsum2 as umsatz
, kosten
, zwsum1 -zwsum2 - kosten as Ergebnis
, ((zwsum1 -zwsum2 - kosten)*100 /(zwsum1 - zwsum2)) as rendite
, (select Count(*) From aauftrag
WHERE Date(aLadetag) = dateAusposten
and status != 3 ) as Sendungen
, dateausPosten
FROM (
SELECT
sum(IF (status != 3 AND status != 4 AND TransAnr != 'Gutschrift' , Gesamt, 0)) as zwsum1
, sum(IF (status != 3 AND TransAnr = 'Gutschrift' AND TYPE = 3 ,Gesamt, 0)) AS zwsum2
, sum(IF (status != 3 AND TransAnr != 'Gutschrift' , Untern_Verguetung, 0)) AS kosten
, date(datum) as dateAusposten
FROM ( SELECT *
FROM posten
WHERE EXTRACT (YEAR_MONTH FROM DATUM) = '{$yearMonth}'
AND RG_Nummer != 'Dienstgut'
) p
GROUP BY DATE (DATUM)
) pg
) pgXSendungen
;
Auf ein GROUP BY der Tabelle aauftrag nach Tagen habe ich verzichtet - für deine max 31 Tage mache ich dann lieber 31x ein Inline-Select mit einem Count-Alles-was-kein-Storno-ist.
Die Aliasnamen ergeben sich als p=posten, pg=postenGruppiert, pgXSendungen=pg mit Sendungen.
Ein künstliches Feld "DateAusPosten" habe ich noch zum Hochreichen in den SELECTs zugefügt.
Ach ja, und dort wo dividiert wird bei den Mittelwertberechungen habe ich noch das DIV/0 abgefangen.
Grüße
Biber
[Edit] In Zeile 13 den Nenner (zwsum1-zwsum2) in Klammern gesetzt... wegen Punkt-vor-Strich-Rechnung [/Edit]
Moin datadexx,
freut mich, wenn es passt.
Aber noch mal meine Rückfrage von oben:
Sind denn "posten.Datum" und "aauftrag.ladetag" nicht ohnehin vom Datentyp "Date"?
Im Moment werden beide (vermutlich überflüssigerweise) gecastet, was sicherlich jegliche Nutzung eines vorhandenen Index unterbindet.
Wenn die schon den richtigen Datentyp haben, dann könnte das Statement ohne diese Date(xxx)-Zwangspressung signifikant schneller fertig werden.
Grüße
Biber
freut mich, wenn es passt.
Aber noch mal meine Rückfrage von oben:
Sind denn "posten.Datum" und "aauftrag.ladetag" nicht ohnehin vom Datentyp "Date"?
Im Moment werden beide (vermutlich überflüssigerweise) gecastet, was sicherlich jegliche Nutzung eines vorhandenen Index unterbindet.
Wenn die schon den richtigen Datentyp haben, dann könnte das Statement ohne diese Date(xxx)-Zwangspressung signifikant schneller fertig werden.
Grüße
Biber
Moin datadexx,
-> auch da würde ich ein paar Sachen anders machen.
Eine Grundregel habe ich oben auch verletzt: immer nur die Felder holen, die ich auch brauche.
Ein "SELECT * from posten" ist zwar fix getippt, aber hier vollkommener Bullshit.
Am Ende des Tages willst du doch von den vermutlich 20 Feldern in "posten" nur dreieinhalb... hier könnte man mit ein ein wenig mehr Tipperei ganz für lau Performance herausholen.
In die selbe Kerbe -Tippfaulheit kostet Performance- geht auf jeden Fall der Parameter "'{$yearMonth}".
Klar, ist schnell eingetippt und ist auch zweifelsohne die "richtige" GROUP BY.Granularität.
Wenn du aber diesen Parameter '{$yearMonth}', also zb '201611' abfragst und rüberbringst als 2 Parameter '{$vonDatum}' und '{$bisDatum}', dann könntest du statt das
...bei dem niemals nich' irgendein Zugriff über Index möglich ist und IMMER die ganze ver###te "posten"-Tabelle von Anbeginn der Zeit bis heute durchgenudelt werden muss (full table scan), ändern in ein...
Da sicherlich ein Index auf dem Datumsfeld liegt (oder innerhalb einer Minute angelegt werden kann), kannst du da relativ schnell ein bisschen tunen.
Denn der eigentliche Auswertungszeitraum ist sehr klein (ein Monat), die gesammelten Posten-Daten dagegen enthalten evtl das Hundertfache (100 Monate sind 8 Jahre ~ Daten seit 2008-> ist nicht unrealistisch).
Wenn Du also gezielt über einen Index auf das eine Hundertstel der Daten zugreifen kannst, das auswertungsrelevant ist, dann bekommst du unter Umständen einen anerkennenden Blick vom Cheffe oder ähnliches.
Frohe Weihnachten
Biber
Ja, beide Felder sind vom Datentyp date.
Dann nimm oben die beiden Date()-Anweisungen raus.Schneller fertig werden hört sich super an...
Falls da irgendwie durchschimmert, dass diese Abfrage nicht so performant ist...-> auch da würde ich ein paar Sachen anders machen.
Eine Grundregel habe ich oben auch verletzt: immer nur die Felder holen, die ich auch brauche.
Ein "SELECT * from posten" ist zwar fix getippt, aber hier vollkommener Bullshit.
Am Ende des Tages willst du doch von den vermutlich 20 Feldern in "posten" nur dreieinhalb... hier könnte man mit ein ein wenig mehr Tipperei ganz für lau Performance herausholen.
In die selbe Kerbe -Tippfaulheit kostet Performance- geht auf jeden Fall der Parameter "'{$yearMonth}".
Klar, ist schnell eingetippt und ist auch zweifelsohne die "richtige" GROUP BY.Granularität.
Wenn du aber diesen Parameter '{$yearMonth}', also zb '201611' abfragst und rüberbringst als 2 Parameter '{$vonDatum}' und '{$bisDatum}', dann könntest du statt das
...WHERE EXTRACT (YEAR_MONTH FROM DATUM) = '{$yearMonth}'
...WHERE DATUM >= '{$vonDatum}' and DATUM <= '{$bisDatum}'
Da sicherlich ein Index auf dem Datumsfeld liegt (oder innerhalb einer Minute angelegt werden kann), kannst du da relativ schnell ein bisschen tunen.
Denn der eigentliche Auswertungszeitraum ist sehr klein (ein Monat), die gesammelten Posten-Daten dagegen enthalten evtl das Hundertfache (100 Monate sind 8 Jahre ~ Daten seit 2008-> ist nicht unrealistisch).
Wenn Du also gezielt über einen Index auf das eine Hundertstel der Daten zugreifen kannst, das auswertungsrelevant ist, dann bekommst du unter Umständen einen anerkennenden Blick vom Cheffe oder ähnliches.
Frohe Weihnachten
Biber