Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Schleifen Optimierung? (https://www.delphipraxis.net/137124-schleifen-optimierung.html)

youuu 14. Jul 2009 21:47


Schleifen Optimierung?
 
Hi,

ich habe ein Problem, am Anfang meines Programms Teste ich den Status einzellner Einträge.

Ausgangssituation.

2 Tabellen
--> Tabelle 1 Kunde
--> Tabelle 2 Positionen (mehrere Einträge möglich pro Kundennummer)

Nun möchte ich testen ob der Kunde noch aktive ist oder inaktive, heißt wenn der Kunde keine aktive position mehr besitzt, dann ist er inaktive.


Ich habe es bislang so gelöst.

Delphi-Quellcode:
  Query.SQL.Text:='SELECT * FROM '+Tabelle.customer+'';
  Query.Open;
  while not Query.Eof do
  begin
    Customer:= TCustomerDaten.create;
    with Customer do
    Begin
      Anrede:= Customer.FieldByName('Anrede').AsString;
      name:= Customer.FieldByName('name').AsString;
    end;
    VstCustomer.Addchild(nil, Customer);

    If StatusCustomer (Customer.Customer_number) Then  // Status updaten
      Customer.status:= 'aktive'
    Else
      Customer.status:= 'inaktive';

    Customer.Next

Hier die Funktion Status Customer

Delphi-Quellcode:
Function StatusCustomer (Customer_Number: String): Boolean;
Var
  Status: TStringlist;
  i: Integer;
Begin
  Status:= TStringlist.Create;
  result:= false;

  Try
    QueryUpdate.SQL.Text:='SELECT * FROM '+Tabelle.position+' Where Customer_Number = '''+Customer_Number+'''';
    QueryUpdate.Open;
      while not QueryUpdate.Eof do
      Begin
        if StrToDate(QueryUpdate.FieldByName('announcement_date').AsString) >= Date() then
          status.Add('aktive')
        else
          status.Add('inaktive');

        QueryUpdate.Next;
      End;

      for i := 0 to status.Count - 1 do
        if Status[i] = 'aktive' then
        Begin
          result:= True;
          exit;
        End
        Else
          result:= false;
  Finally
    QueryUpdate.Close;
    Status.Free;
  End;
End;

So entstehen bei 10.000 Kunden a 40.000 Positionen extreme lange Wartezeiten.

Kann mir jemand einen Tipp geben um dies zu beschleunigen?

mkinzler 14. Jul 2009 21:52

Re: Schleifen Optimierung?
 
Verwende (SQL-)Paramter.
Die Schleife scheint mir zudem überflüssig, weil result auch direkt setzen kannst.

youuu 14. Jul 2009 21:57

Re: Schleifen Optimierung?
 
Stimmt habe nun nur noch:

Delphi-Quellcode:
Function StatusCustomer (Customer_Number: String): Boolean;
Var
  Status: TStringlist;
  i: Integer;
Begin
  Status:= TStringlist.Create;
  result:= false;

  Try
    FormMain.QueryUpdate.SQL.Text:='SELECT * FROM '+Tabelle.position+' Where Customer_Number = '''+Customer_Number+'''';
    FormMain.QueryUpdate.Open;
      while not FormMain.QueryUpdate.Eof do
      Begin
        if StrToDate(FormMain.QueryUpdate.FieldByName('announcement_date').AsString) >= Date() then
          result:= true;
        FormMain.QueryUpdate.Next;
      End;
  Finally
    FormMain.QueryUpdate.Close;
    Status.Free;
  End;
Aber was gena meintest du mit SQL Paremeter benutzen?

mkinzler 14. Jul 2009 21:59

Re: Schleifen Optimierung?
 
SQL-Code:
SELECT * FROM '+Tabelle.position+' Where Customer_Number = :Customer_Number;

youuu 14. Jul 2009 22:14

Re: Schleifen Optimierung?
 
Dadurch wird aber die Abfrage nicht schneller gestaltet oder?

Trotz der Schleifen Änderung, bin ich bei den momentanen 670 Kunden und 1400 Positionen schon bei knapp 1 Minute Wartezeit.

mkinzler 14. Jul 2009 22:20

Re: Schleifen Optimierung?
 
Zitat:

Dadurch wird aber die Abfrage nicht schneller gestaltet oder?
In diesem Teil nicht, ist aber übersichtlicher
Ich würde noch
Delphi-Quellcode:
StrToDate(FormMain.QueryUpdate.FieldByName('announcement_date').AsString)
durch
Delphi-Quellcode:
FormMain.QueryUpdate.FieldByName('announcement_date').AsDate
oder noch besser
Delphi-Quellcode:
FormMain.QueryUpdate.FieldByName('announcement_date').Value
ersetzen.
Zudem könnte man die Abfrage schon auf announcement_date > Datum einschränken.
U.U könnte auch die Db Struktur geändert werden ( Warum mehrere gleiche Tabellen?; sieht für mich so aus)

Satty67 14. Jul 2009 22:20

Re: Schleifen Optimierung?
 
Glaube SQL-Parameter ist ein allgemein gemeinter Verbesserungsvorschlag ;)
...und reicht nicht auch ein
SQL-Code:
SELECT announcement_date FROM ...
€: Der Thread schlummerte wohl zu lange im Tab, keine rote Box

youuu 14. Jul 2009 22:32

Re: Schleifen Optimierung?
 
Für Verbeserungsvorschläge bin ich imer offen ;)

Ich wüsste nicht wie ich dies in einer Tabelle unterbingen sollte, da jeder Kunde mehrere Positionen(Rechnungen) haben kann.

Ansonsten noch Ideen für die Beschleunigung oder soll ich gar lieber eine Art Splashscreen mit dem Status laufen lassen?

generic 14. Jul 2009 22:35

Re: Schleifen Optimierung?
 
Ich würde die Unterabfrage seinen lassen und direkt in die erste joinen lassen.
Das sollte viel schneller gehen.

youuu 14. Jul 2009 22:37

Re: Schleifen Optimierung?
 
Zitat:

Zitat von generic
Ich würde die Unterabfrage seinen lassen und direkt in die erste joinen lassen.
Das sollte viel schneller gehen.

Wie meinst du das?

Blup 15. Jul 2009 08:13

Re: Schleifen Optimierung?
 
z.B. so:
SQL-Code:
SELECT c.*,
  (SELECT count(p.Customer_Number)
   FROM  Position p
   Where p.Customer_Number = c.Customer_Number) Anzahl
FROM Customer c
[edit=mkinzler]Code-Tag durch SQL-Tag ersetzt Mfg, mkinzler[/edit]

generic 15. Jul 2009 09:51

Re: Schleifen Optimierung?
 
^^^ Subquerys gehen natürlich auch.

Ich meinte eher mit den SQL Befehlen/Funktionen max,[left] join, group by zu arbeiten.

Deine eine Schleife könnte man beschleunigen, in dem du in der IF-Abfrage hinter den result:=true ein break schreibst.
Somit müsste nicht durch jeden Datensatz gegangen werden.

Weitere Optimierungen sehe ich in dieser Bedingung:
Delphi-Quellcode:
StrToDate(FormMain.QueryUpdate.FieldByName('announcement_date').AsString) >= Date()
Warum muss das im Code passieren? Warum machst du das nicht direkt im SQL.
Somit müsste der Server dir weniger Daten liefern und du bräuchtest nur noch prüfen ob Daten da sind oder nicht.

Allerdings ich würde meinen ersten Vorschlag bevorzugen, da dieser nur einen SQL Abfrage zu Server schickt.

GHorn 15. Jul 2009 10:01

Re: Schleifen Optimierung?
 
Von generic: Break in der while-Schleife

Wenn Du dann noch nach dem Datumfeld sortierst, bist Du schnell fertig.


SQL-Code:
    FormMain.QueryUpdate.SQL.Text:='SELECT * FROM '+Tabelle.position+' Where Customer_Number = '''+Customer_Number+'''' order by announcement_date DESC

Gruß
Gerald

Tyrolean 15. Jul 2009 10:24

Re: Schleifen Optimierung?
 
Zitat:

Zitat von Blup
z.B. so:
SQL-Code:
SELECT c.*,
  (SELECT count(p.Customer_Number)
   FROM  Position p
   Where p.Customer_Number = c.Customer_Number) Anzahl
FROM Customer c

Eigentlich am schnellsten müsste sein

SQL-Code:
SELECT c.*,
  (SELECT count(p.Customer_Number)
   FROM  Position p
   Where p.Customer_Number = c.Customer_Number and p.announcement_date > Date()) Anzahl
FROM Customer c

Uwe Raabe 15. Jul 2009 10:38

Re: Schleifen Optimierung?
 
Mein SQL ist zwar gerade etwas eingerostet, aber geht es nicht einfacher (schneller) so?

Delphi-Quellcode:
Function StatusCustomer (Customer_Number: String): Boolean;
Begin
  result:= false;

  FormMain.QueryUpdate.SQL.Text := 'SELECT Count(*) AS Anzahl FROM ' + Tabelle.position +
      ' Where (Customer_Number = '''+Customer_Number+''') and (announcement_date >= '''+Date()+''')' ;
  FormMain.QueryUpdate.Open;
  Try
    result := (FormMain.QueryUpdate.FieldByName('Anzahl').AsInteger > 0);
  Finally
    FormMain.QueryUpdate.Close;
  End;
End;
Ergänzend schlage ich auch die Verwendung von Parametern vor, da gerade im Fall der wiederholten Abfrage (hier für jeden Kunden) ein Performancegewinn erzielt werden kann. Dabei würde man den SQL.Text entweder statisch in der IDE oder dynamisch vor der while-Schleife über die Kunden zuweisen. Bei der ersten Abfrage macht der SQL-Server dann ein Prepare, was beim wiederholten Aufruf mit anderen Parameterwerten dann nicht mehr gemacht werden muss.

Delphi-Quellcode:
 
  FormMain.QueryUpdate.SQL.Text := 'SELECT Count(*) AS Anzahl FROM ' + Tabelle.position +
      ' Where (Customer_Number = :Customer_Number) and (announcement_date >= :Date)' ;
Die Funktion reduziert sich dann auf

Delphi-Quellcode:
Function StatusCustomer (Customer_Number: String): Boolean;
Begin
  result:= false;
  FormMain.QueryUpdate.Params.ParamValues['Customer_Number'] := Customer_Number;
  FormMain.QueryUpdate.Params.ParamValues['Date'] := Date();
  FormMain.QueryUpdate.Open;
  Try
    result := (FormMain.QueryUpdate.FieldByName('Anzahl').AsInteger > 0);
  Finally
    FormMain.QueryUpdate.Close;
  End;
End;

jbg 15. Jul 2009 12:30

Re: Schleifen Optimierung?
 
Noch ein Tipp: FieldByName('Anrede') ist bei Delphi 2009 extrem langsam, da die Heinis vergessen haben das "WideCompareText" durch ein "AnsiCompareText" zu ersetzen und somit der Feldname (UnicodeString) FieldCount-mal in einen WideString umgewandelt wird.

Wenn du das dann in einer Schleife anwendest, wäre es besser sich das TField vorher einmalig zu holen und dann direkt mit dem Field zu arbeiten.

p80286 15. Jul 2009 14:06

Re: Schleifen Optimierung?
 
Entschuldigt, aber die Ausgangsfrage war doch herauszufinden ob ein Kunde noch aktiv ist?
Dann sollte so etwas eigentlich ausreichen:

SQL-Code:
select distinct Kundenname
from KundendTab,PositionTab
where KundenTab.KundenID=PositionTab.KundenID
  and Position.Datum>sysdate
(Ich hab es nicht so mit den Joins, ich hoffe es ist trotzdem klar worauf ich raus will)

Dann hat man schon einmal eine Liste der "aktiven" Kunden, und wer nicht aktiv ist, ist halt "inaktiv".

Gruß
K-H

youuu 15. Jul 2009 19:09

Re: Schleifen Optimierung?
 
Delphi-Quellcode:
Function StatusCustomer (Customer_Number: String): Boolean;
Begin
  result:= false;
  FormMain.QueryUpdate.Params.ParamValues['Customer_Number'] := Customer_Number;
  FormMain.QueryUpdate.Params.ParamValues['Date'] := Date();
  FormMain.QueryUpdate.Open;
  Try
    result := (FormMain.QueryUpdate.FieldByName('Anzahl').AsInteger > 0);
  Finally
    FormMain.QueryUpdate.Close;
  End;
End;
[/quote]

Wobei ich damit Probleme bekomme, da MySql irgendwi eine andere Formatierung benutzt für Date.

Hate davor alles in Varchar und hab es für diese funktion in Date geändert, da er mit Varchar keine richtigen ergenisse seiten aktive und inaktive mehr raus geworfen hat, allerdings auch nicht mit Date.


Date in MySQL sieht so aus: 2009-07-19 z.B, wobe in Date doch 19.07.2009

mkinzler 15. Jul 2009 19:16

Re: Schleifen Optimierung?
 
Notfalls halt
Delphi-Quellcode:
FormMain.QueryUpdate.Params.ParamByName('Date').Value := Date();

youuu 15. Jul 2009 19:38

Re: Schleifen Optimierung?
 
Würde es gern schon richtig machen.
Nur wie kann ich das anstellen das Delphi und MySQL Datum übereinstimmen ohne dabei auf dd.mm.yyyy verzichten zumüssen?

mkinzler 15. Jul 2009 19:39

Re: Schleifen Optimierung?
 
Deshalb ja der Parameter, der für die richtige Übergabe sorgt

youuu 15. Jul 2009 20:00

Re: Schleifen Optimierung?
 
Das funktionierte alleridngs gerade nicht, obwohl ich ei veralteertes Datum in Mysql speicherte gab er "aktive" aus.

Auch wenn es nur 1 Tag veraltert war, muss das ja richtig funktionieren.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:55 Uhr.

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