Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   SQL-Statement zu aufwendig (https://www.delphipraxis.net/180537-sql-statement-zu-aufwendig.html)

Ykcim 27. Mai 2014 15:36

Datenbank: MySQL • Version: 5 • Zugriff über: UniDac

SQL-Statement zu aufwendig
 
Hallo Zusammen,

ich habe eine Tabelle, in der die Artikel und die Kundenbedarfe stehen. Jetzt möchte ich gerne mit einer Query für jeden Disponenten folgende Daten holen:
ArtikelNr, Lieferungen die überfällig sind (Lieferdatum<aktuelles Datum), Summe der Mengen je Artikel für aktuellen Monat, Summe der Mengen je Artikel für nächsten Monat, Summe der Mengen je Artikel für übernächsten Monat.

Ich habe folgendes ausprobiert, aber neben der Tatsache, dass die Werte nicht stimmten, benötigt die Query auch über 30min.... WIe kann ich das geschickter machen?

Delphi-Quellcode:
select concat(liefersituation.watenr,' ',liefersituation.tebez1) as 'Artikel Bezeichnung', sum(t1.ltrest) as Rückstand, sum(t2.ltrest) as 'Bedarf aktueller Monat', sum(t3.ltrest) as 'Bedarf nächster Monat', sum(t4.ltrest) as 'Bedarf übernächster Monat' from liefersituation
left join liefersituation as t1 on t1.watenr=liefersituation.watenr and t1.ltlite<current_date
left join liefersituation as t2 on t2.watenr=liefersituation.watenr and month(t2.ltlite)=month(current_date)
left join liefersituation as t3 on t3.watenr=liefersituation.watenr and month(t3.ltlite)=month(current_date)+1
left join liefersituation as t4 on t4.watenr=liefersituation.watenr and month(t4.ltlite)=month(current_date)+2
where liefersituation.lpdivk='ssc' //Disponenten-Kürzel
group by liefersituation.watenr
order by liefersituation.watenr


Vielen Dank
Patrick

DeddyH 27. Mai 2014 15:48

AW: SQL-Statement zu aufwendig
 
Wie ist es, wenn Du den Disponenten als JOIN-Bedingung hinzufügst?

arnof 27. Mai 2014 15:57

AW: SQL-Statement zu aufwendig
 
Zitat:

Zitat von Ykcim (Beitrag 1260453)
Hallo Zusammen,

ich habe eine Tabelle, in der die Artikel und die Kundenbedarfe stehen. Jetzt möchte ich gerne mit einer Query für jeden Disponenten folgende Daten holen:
ArtikelNr, Lieferungen die überfällig sind (Lieferdatum<aktuelles Datum), Summe der Mengen je Artikel für aktuellen Monat, Summe der Mengen je Artikel für nächsten Monat, Summe der Mengen je Artikel für übernächsten Monat.

Ich habe folgendes ausprobiert, aber neben der Tatsache, dass die Werte nicht stimmten, benötigt die Query auch über 30min.... WIe kann ich das geschickter machen?

Delphi-Quellcode:
select concat(liefersituation.watenr,' ',liefersituation.tebez1) as 'Artikel Bezeichnung', sum(t1.ltrest) as Rückstand, sum(t2.ltrest) as 'Bedarf aktueller Monat', sum(t3.ltrest) as 'Bedarf nächster Monat', sum(t4.ltrest) as 'Bedarf übernächster Monat' from liefersituation
left join liefersituation as t1 on t1.watenr=liefersituation.watenr and t1.ltlite<current_date
left join liefersituation as t2 on t2.watenr=liefersituation.watenr and month(t2.ltlite)=month(current_date)
left join liefersituation as t3 on t3.watenr=liefersituation.watenr and month(t3.ltlite)=month(current_date)+1
left join liefersituation as t4 on t4.watenr=liefersituation.watenr and month(t4.ltlite)=month(current_date)+2
where liefersituation.lpdivk='ssc' //Disponenten-Kürzel
group by liefersituation.watenr
order by liefersituation.watenr


Vielen Dank
Patrick

der group by stimmt nicht!

i.d.R. musst Du das groupen was Du im select anzeigst, wenn es keine agregatfunktion ist.

Bei MS SQL würde die Anweisung gar nicht so gehen, mysql macht hier was, aber wie Du schon gemerkt hast stimmt das Ergebnis nicht!

Jumpy 27. Mai 2014 16:05

AW: SQL-Statement zu aufwendig
 
Hier wäre es vllt. einfacher das über Subselects zu lösen, denn wie arnof schon sagte, passt a) das Gruppieren nicht und b) stehen wahrscheinlich in dem Join mehr/weniger Datensätze drin, als eigentlich sollten. Kann man ja mal mit einem "Select *" prüfen.

Ykcim 27. Mai 2014 16:25

AW: SQL-Statement zu aufwendig
 
Hallo,

keine Chance, der MySQL-Server kommt damit einfach nicht zurecht... Ich habe jetzt schon wieder ca. 30 min. gewartet - er ist noch nicht fertig...

Gruß
Patrick

jobo 27. Mai 2014 17:19

AW: SQL-Statement zu aufwendig
 
Du verwendest auf einigen Spalten eine Funktion (month), um das Ergebnis mit einer weiteren Funktion zu vergleichen.
Month(currentdate) usw. sollte kein Problem sein (Kopfrechnen), sowas wie month(<spalte>) macht aber idR die (hoffentlich vorhandenen) Indices unbrauchbar.
Wenn Du das umformst nach
Code:
<spalte> between [anfang aktueller Monat] and current_date
könnte es deutlich schneller laufen.
Bevor Du nun lange bastelst, um den aktuellen Monatsanfang auszurechnen, probier es einfach mal mit festen Werten aus, vlt. "current_date - 27" (das wäre heute der 1.Mai.), ich kenne die Datumsfunktionen von mySQL leider nicht auswendig.

himitsu 27. Mai 2014 21:07

AW: SQL-Statement zu aufwendig
 
Mal so als Tipp: Lass' alle Aggregatfunktionen (Sum) weg und auch das GroupBy muß kurz raus.
Und jetzt schau dir mal an, was deine Joints eingentlich veranstalten.
(ich glaub nicht, daß es dem entspricht, was du willst)

Im Grunde müssen die SUM-Felder also eher SubSelects sein, würde ich mir mal so denken.

PS: Als [code=sql]...[/code] macht sich der obrige "Delphi"-Code bestimmt auch viel besser.

Dejan Vu 27. Mai 2014 21:25

AW: SQL-Statement zu aufwendig
 
Versuch es mal ganz ohne join:
SQL-Code:
select concat(watenr,' ',tebez1) as 'Artikel Bezeichnung',
   sum(iif(ltlite < current_date, ltrest,0) as Rückstand,
   sum(iif(month(ltlite) = month(current_date)   ,ltrest,0) as 'Bedarf aktueller Monat',
   sum(iif(month(ltlite) = month(current_date) + 1,ltrest,0) as 'Bedarf nächster Monat',
   sum(iif(month(ltlite) = month(current_date) + 2,ltrest,0) as 'Bedarf übernächster Monat'
from liefersituation
group by concat(watenr,' ',tebez1)
Bezüglich der Syntax musst du das ggf. anpassen. Kann kein mist-QL.
Wichtig ist, das in deinem Group By- Ausdruck alle Ausdrücke angegeben sein müssen, die nicht aggregiert werden.

Das dürfte so auch mit deiner 'Month' Klausel, da er eh nur 1x durch die Tabelle rennt.

PS: Dein 'Bedarf aktueller Monat' enthält auch den Rückstand des aktuellen Monats....
also vielleicht alternativ...
SQL-Code:
...
   sum(iif(ltlite >=current_date and month(ltlite) = month(current_date)   ,ltrest,0) as 'Bedarf aktueller Monat',
...

arnof 27. Mai 2014 21:31

AW: SQL-Statement zu aufwendig
 
Zitat:

Zitat von Ykcim (Beitrag 1260468)
Hallo,

keine Chance, der MySQL-Server kommt damit einfach nicht zurecht... Ich habe jetzt schon wieder ca. 30 min. gewartet - er ist noch nicht fertig...

Gruß
Patrick

Das nennt man dann Datenbankdesign. Zur Analyse warum da was nicht geht bzw zu langsam ist, hat emba die entsprechenden Tools.

Beispiel aus meiner Praxis: SQL Abfrage aufgrund von Datenmengen beim Kunden nicht benutzbare 30 sec, durch setzen der Indexe passend zur Abfrage auf nicht mehr fühlbare 0.01 sec reduziert ohne eine Zeile Delphicode zu ändern!

Dejan Vu 27. Mai 2014 21:36

AW: SQL-Statement zu aufwendig
 
Meist benötigt man dazu keine Tools. Scharfes hinschauen genügt. Und wenn man etwas faul ist, scharfes Betrachten des Queryplans. Die von mir vorgeschlagene Optimierung (von der ich arroganterweise annehme, das sie eine schnellere Möglichkeit ist) bekommt ein Tool eh nicht hin. Behaupte ich mal.

Betonung allerdings auf 'meist', also für professionelle Optimierung als Lebensaufgabe oder Berufsdefinition sind solche Tools durchaus brauchbar.

himitsu 27. Mai 2014 21:48

AW: SQL-Statement zu aufwendig
 
Wobei dein Code doch auch nur geht, wenn es für jeden Artikel (watenr+tebez1) maximal je einen Eintrag pro Zeitraum gibt. (und das sollten die ursprünglichen Joins auch hinbekommen, wenn vielleicht auch etwas langsamer).

Wobei bei den Joins alle Zeiträume in je einem Artikeldatensatz zusammen sein müssen, da sie sonst doppelt/mehrfach eingerecht werden.

hstreicher 28. Mai 2014 06:34

AW: SQL-Statement zu aufwendig
 
Die Ergebniss ist wahrscheinlich falsch weil da nur auf Monat verglichen wird , d.h. es wir Mai 2014+Mai2013 +Mai2012 usw aufaddiert
da muss noch ein Vergleich auf Jahr hinein

Zur Geschwindigkeit, das extract kann keinen Index verwenden es führt immer zu einem Full Table Scan
schneller wird wenn man die Bedingungen mit

2014-05-01<=Liefertermin<=2014-05-31

eingrenzt dann kann ein Index auf Liefertermin verwendet werden und das Ergebniss stimmt auch

mfg Hannes

Dejan Vu 28. Mai 2014 07:04

AW: SQL-Statement zu aufwendig
 
Zitat:

Zitat von hstreicher (Beitrag 1260535)
Die Ergebnisse sind wahrscheinlich falsch

Korrekt. Mein erster Ansatz war mit 'between' und dem vorherigen Ausrechnen der Monatsgrenzen, also so:

SQL-Code:
  ...
  SUM(IIF (ltlite between @firstOfMonth and @lastOfMonth, ltRest,0)) as 'Bedarf Monat'
  ...
Aber ich dachte, wenn er seine Monate haben will, soll er doch. Die Eingrenzung des Lieferdatums funktioniert wegen der Spalte 'Rückstände' nicht, denn man weiß nicht, wann der älteste Rückstand ist.

Bei einer Aggregierung über die Tabelle ist ein Scan zwingend. Bei sehr großen Tabellen könnte ein Index auf den Aggregatspalten etwas bringen, aber das dürfte auf das RDBMS ankommen.

Sir Rufo 28. Mai 2014 07:08

AW: SQL-Statement zu aufwendig
 
In solchen Fällen kann es auch helfen, wenn man mit temporären Tabellen arbeitet.

Die zu untersuchende Datenmenge wird mit einem einfachen SELECT in eine auf die Suche passendes Format gebracht (z.B. der Monat) und eingeschränkt. Dann werden dort passende Indizes für die Suche (Spalte Monat) erstellt und mit dieser reduzierten Datenmenge wird dann die konkrete Abfrage durchgeführt.

BTW:

Trotz allem können solche Abfragen je nach Datenvolumen eine gewisse Zeit in Anspruch nehmen. Dann lässt man diese nicht synchron am Client laufen, sondern asynchron auf dem Server (eigene Anwendung).

Dejan Vu 28. Mai 2014 07:40

AW: SQL-Statement zu aufwendig
 
Zitat:

Zitat von Sir Rufo (Beitrag 1260538)
In solchen Fällen kann es auch helfen, wenn man mit temporären Tabellen arbeitet.

Ja, aber imho nicht bei diesem Problem.
Zitat:

...Dann lässt man diese nicht synchron am Client laufen, sondern asynchron auf dem Server (eigene Anwendung).
Yo, aber nur, wenn man das 1x pro Monat macht, imho. Ansonsten würde ich Redundanz schaffen, aka DWH: Im Kleinstformat reicht hier eine Tabelle 'Bestellbestand' mit den Spalten der gewünschten Ausgabe und täglicher Pflege über einen Mitternachtslauf oder real-time über Trigger. Hier würde ich jedoch sogar eine Historie anlegen, mit den Spalten 'Datum, Artikel, Zeitraum, Bestand'. 'Datum' ist das heutige Datum, 'Zeitraum' ist 'Rückstand' bzw. Monat+Jahr und 'Bestand' eben der Bestand des Zeitraums. Diese Tabelle wächst täglich. Mit ihr kann ich mir eine Historie der Bestellübersichten anschauen und z.B. einschreiten, wenn sich 'Rückstände' tendenziell erhöhen oder zukünftige Bestellbestände abnehmen...

Die o.g. Auswertung erhalte ich dann über einen PIVOT

quaero 28. Mai 2014 08:59

AW: SQL-Statement zu aufwendig
 
Zitat:

Zitat von Ykcim (Beitrag 1260453)
Hallo Zusammen,

ich habe eine Tabelle, in der die Artikel und die Kundenbedarfe stehen. Jetzt möchte ich gerne mit einer Query für jeden Disponenten folgende Daten holen:
ArtikelNr, Lieferungen die überfällig sind (Lieferdatum<aktuelles Datum), Summe der Mengen je Artikel für aktuellen Monat, Summe der Mengen je Artikel für nächsten Monat, Summe der Mengen je Artikel für übernächsten Monat.

Ich habe folgendes ausprobiert, aber neben der Tatsache, dass die Werte nicht stimmten, benötigt die Query auch über 30min.... WIe kann ich das geschickter machen?

Delphi-Quellcode:
select concat(liefersituation.watenr,' ',liefersituation.tebez1) as 'Artikel Bezeichnung', sum(t1.ltrest) as Rückstand, sum(t2.ltrest) as 'Bedarf aktueller Monat', sum(t3.ltrest) as 'Bedarf nächster Monat', sum(t4.ltrest) as 'Bedarf übernächster Monat' from liefersituation
left join liefersituation as t1 on t1.watenr=liefersituation.watenr and t1.ltlite<current_date
left join liefersituation as t2 on t2.watenr=liefersituation.watenr and month(t2.ltlite)=month(current_date)
left join liefersituation as t3 on t3.watenr=liefersituation.watenr and month(t3.ltlite)=month(current_date)+1
left join liefersituation as t4 on t4.watenr=liefersituation.watenr and month(t4.ltlite)=month(current_date)+2
where liefersituation.lpdivk='ssc' //Disponenten-Kürzel
group by liefersituation.watenr
order by liefersituation.watenr


Vielen Dank
Patrick

Du könntest zunächst eine Anfrage an die DB schicken, die bereits alle Informationen enthält:
SQL-Code:
SELECT concat(watenr,' ',tebez1) AS 'Artikel Bezeichnung', year(ltlite) AS Jahr, month(ltlite) AS Monat, SUM(ltrest) AS Menge
FROM liefersituation
WHERE liefersituation.lpdivk='ssc' //Disponenten-Kürzel
  AND ((year(ltlite) < year(dateadd(m, 2, getdate()))) // vergangene Jahre vor übernächsten Monat komplett
     OR ((year(ltlite) = year(dateadd(m, 2, getdate()))) AND (month(ltlite) <= month(dateadd(m, 2, getdate()))))) // aktuelles Jahr das übernächsten Monats bis zu diesem
group by concat(watenr,' ',tebez1), year(ltlite), month(ltlite)
order by concat(watenr,' ',tebez1), year(ltlite), month(ltlite)
Damit hättest du erst einmal die Mengen der einzelnen Produkte der jeweiligen Monate bis einschließlich übernächsten Monat.
Aus dem Ergebnis müsstest du jetzt noch alles, was zum aktuellen Monat gehört in eine eigene Spalte ausgliedern, alles vom nächsten Monat in eine weitere und alles vom übernächsten Monat in noch eine:
SQL-Code:
SELECT [Artikel Bezeichnung], Monat, Jahr, Menge AS Rückstand, 0 AS 'aktueller Monat', 0 AS 'nächster Monat', 0 AS 'übernächster Monat'
FROM ergebnis1 // erste SQL-Anfrage
WHERE (Jahr < year(getdate())) // vergangene Jahre
  OR ((Jahr = year(getdate())) AND (Monat < month(getdate()))) // vergangene Monate des aktuellen Jahres

UNION
SELECT [Artikel Bezeichnung], Monat, Jahr, 0 AS Rückstand, Menge AS 'aktueller Monat', 0 AS 'nächster Monat', 0 AS 'übernächster Monat'
FROM ergebnis1 // erste SQL-Anfrage
WHERE Jahr = year(getdate()) AND Monat = month(getdate())

UNION
SELECT [Artikel Bezeichnung], Monat, Jahr, 0 AS Rückstand, 0 AS 'aktueller Monat', Menge AS 'nächster Monat', 0 AS 'übernächster Monat'
FROM ergebnis1 // erste SQL-Anfrage
WHERE Jahr = year(dateadd(m, 1, getdate())) AND Monat = month(dateadd(m, 1, getdate()))

UNION // übernächster Monat
 ...
Anschließend kannst du über dieses 2. Ergebnis aggregieren und solltest das gewünschte Ergebnis erhalten:
SQL-Code:
SELECT [Artikel Bezeichnung], SUM([Rückstand]), SUM([aktueller Monat]), SUM([nächster Monat]), SUM([übernächster Monat])
FROM ergebnis2 // zweite SQL-Anfrage
GROUP BY [Artikel Bezeichnung]

Bei Bedarf kannst du für das erste Ergebnis auch eine Sicht bilden und die zweite und dritte Anfrage zu einer zusammenfassen.
Grüße
quaero


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:54 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz