Performance-Problem bei Get-Befehl

27. Juni 2007 11:01

Hallo zusammen,

ein Get-Befehl auf einer Lokalen Datenbank dauert nicht annähernd so lange wie auf einer Server DB! Auf der Lokalen geht´s ruckzuck und auf der Server DB dauert es vergleichsweise ewig!

Meine Funktion durchsucht die Artikeltabelle und schreibt die Nummern, die nicht mit Get gefunden werden in eine temporäre Tabelle.

Hat jemand Ideen, wie ich die Geschwindigkeit eines Get-Befehls erhöhen könnte?

Mit Setcurrentkey den Schlüssel vorher festlegen hat nichts gebracht.

Vielen Dank schon mal.

27. Juni 2007 11:15

Vielleicht erst einmal grundsätzlich...
Du möchtest alle Datensätze, die nicht "direkt gefunden" werden, in eine tmep. Tabelle zwischenspeichern.

Wie sieht nun dein Quelltext dazu aus? Irgendwie scheint es mir, als müsste das auch performanter gehen also mit lauter GETs hintereinander ...

27. Juni 2007 11:25

Hi Natalie,

mein Code sieht vereinfacht so aus:

Code:
CLEAR(IndexNr);
NrFreiTab.RESET;
NrFreiTab.DELETEALL;
Untergrenze := '1000';
Obergrenze := '9999';
WITH ArtikelTab DO BEGIN
   CLEAR(IndexNrCode);
   EVALUATE(IndexNr,Untergrenze);
   REPEAT
         IF IndexNrCode = '' THEN IndexNrCode := FORMAT(IndexNr);
      CLEAR(i);
      IF NOT GET(IndexNrCode) THEN i := 2;
      IF (GET(IndexNrCode) AND ("Beschreibung 2" = 'FREI')) THEN i := 1;
      IF i <> 0 THEN BEGIN
         NrFreiTab."Nr." := IndexNrCode;
         NrFreiTab.Bereich := i;
         NrFreiTab.Bezugstabelle := TABLENAME;
         NrFreiTab.INSERT(TRUE);
       IndexNr := IndexNr + 1;
       CLEAR(IndexNrCode);
   UNTIL FORMAT(IndexNr) = Obergrenze;
END;

Du hast eine bessere Lösung als Get?
Zuletzt geändert von Dune am 27. Juni 2007 11:46, insgesamt 1-mal geändert.

27. Juni 2007 11:27

Kannst du um deinen Code bitte ein [code ]Dein Code[/ code] ohne die Leerzeichen) setzen und alles entsprechend formatieren? Kann man sonst so gut wie gar nicht lesen...

27. Juni 2007 11:47

Sorry, erledigt!

27. Juni 2007 11:54

So würde ich ihn formatieren:


Code:
CLEAR(IndexNr);
NrFreiTab.RESET;
NrFreiTab.DELETEALL;
Untergrenze := '1000';
Obergrenze := '9999';

WITH ArtikelTab DO BEGIN
  CLEAR(IndexNrCode);
  EVALUATE(IndexNr,Untergrenze);
  REPEAT
    IF IndexNrCode = '' THEN
      IndexNrCode := FORMAT(IndexNr);
    CLEAR(i);
    IF NOT GET(IndexNrCode) THEN
       i := 2;
    IF (GET(IndexNrCode) AND ("Beschreibung 2" = 'FREI')) THEN
       i := 1;
    IF i <> 0 THEN BEGIN
      NrFreiTab."Nr." := IndexNrCode;
      NrFreiTab.Bereich := i;
      NrFreiTab.Bezugstabelle := TABLENAME;
      NrFreiTab.INSERT(TRUE);
       // --- Fehlt hier (oder woanders) nicht das END für THEN BEGIN? --
      IndexNr := IndexNr + 1;
      CLEAR(IndexNrCode);
   UNTIL FORMAT(IndexNr) = Obergrenze;
END;


Ich blicke aber trotzdem nicht durch. Kannst du vielleicht nocb einmal und nur in Worten erklären was durchlaufen werden soll und warum?

27. Juni 2007 11:57

Sieht schön aus! Du hast recht, da fehlt genau da ein END;

Aber nun zu deinem Lösungsansatz. Du würdest das ohne Get machen?

27. Juni 2007 11:58

Dune hat geschrieben:Aber nun zu deinem Lösungsansatz. Du würdest das ohne Get machen?

Siehe oben (habs editiert) - ich verstehe leider noch überhaupt nicht, was der Code leisten soll.

--- EDIT ----
bzw. nach welchen genauen Kriterien sollen Datensätze im NrFreiTab landen? Ich lese da was von FREI ...

27. Juni 2007 12:03

Sorry, hatte deinen letzten Satz wohl etwas überlesen.

Diese Funktion durchläuft einen Bereich (Untergrenze bis Obergrenze) in der Tabelle "Artikel" und kontrolliert für jede Nummer:
1. ob diese existiert oder
2. wenn diese existiert, ob in Feld "Beschreibung 2" "FREI" steht.

Im Beispielcode wird bei 1000 angefangen, dann kommt 1001, dann 1002 usw. bis 9999.

Wenn eine dieser Bedingungen erfüllt ist, wird diese Nummer mit ein paar anderen Daten in eine temporäre Tabelle geschrieben.

27. Juni 2007 12:14

Ah, jetzt wirds klarer *g*

ich glaube, ich würds in etwa so machen:
Lösung mit einer einzigen Datenbankabfrage
Code:
NrFreiTab.RESET;
NrFreiTab.DELETEALL;
Untergrenze := '1000';
Obergrenze := '9999';
Letzte_Artikelnummer := Untergrenze;

WITH ArtikelTab DO BEGIN
  SETRANGE("No.", Untergrenze, Obergrenze);
  IF FIND('-') THEN
    REPEAT
      // Artikelnummer in Integer umwandeln = IntNo
       IF IntNo - letzte_Artikelnummer = 1 THEN
         // prüfen, ob FREI, dann einfügen
       ELSE BEGIN         
         FOR i := letzte_artikelnummer + 1 to IntNo - 1 DO
           //NrFreiTab füllen.
       END;
      Letzte_Artikelnummer := IntNo;
    UNTIL NEXT = 0;
END;


---- EDIT ----
Was noch fehlt, ist die Abfrage, ob die letzte Artikelnummer kleiner der Obergrenze ist. Dann noch mehr in NrFreiTab einfügen ...
Zuletzt geändert von Natalie am 27. Juni 2007 12:39, insgesamt 2-mal geändert.

27. Juni 2007 12:18

Übrigens, du hättest dir einige GETS sparen können, wenn du
Code:
IF NOT GET(IndexNrCode) THEN
       i := 2;
    IF (GET(IndexNrCode) AND ("Beschreibung 2" = 'FREI')) THEN
       i := 1;


in folgendes geändert hättest:
Code:
IF NOT GET(IndexNrCode) THEN
       i := 2
ELSE
    IF "Beschreibung 2" = 'FREI' THEN
       i := 1;

27. Juni 2007 12:22

Ich würde das so machen:
(Achtung! Untergrenze und Obergrenze sind jetzt integer!)
Wenn 9999 auch noch geprüft werden soll, musst du Obergrenze eins höher setzen.
Code:
CLEAR(IndexNr);
NrFreiTab.RESET;
NrFreiTab.DELETEALL;
Untergrenze := 1000;
Obergrenze := 9999;

WITH ArtikelTab DO BEGIN
  IndexNr:=Untergrenze;
  REPEAT
     IndexNrCode := FORMAT(IndexNr);
     CLEAR(i);
     IF NOT GET(IndexNrCode) THEN
        i := 2
     ELSE IF "Beschreibung 2" = 'FREI') THEN
        i := 1;
     IF i > 0 THEN BEGIN
       NrFreiTab."Nr." := IndexNrCode;
       NrFreiTab.Bereich := i;
       NrFreiTab.Bezugstabelle := TABLENAME;
       NrFreiTab.INSERT(TRUE);
     END;
     IndexNr += 1;
   UNTIL IndexNr = Obergrenze;
END;

ein Get bleibt zwar, aber die ständige hin und herwandlung von String nach integer wird reduziert. Ganz ohne Get wirds kompliziert, denn du brauchst ja den Inhalt, wenn der Datensatz existiert.

27. Juni 2007 12:33

Jetzt frag ich mich, was schneller ist:
"Eure" vielen GETs (und damit sehr viele Datenbankabfragen) oder "meine" einzige Datenbankabfrage mit sehr vielen Datensätzen als Ergebnis...

27. Juni 2007 12:33

Danke, deine Anmerkung über die Gets ist richtig. Hab ich schon übernommen! Bringt schon mal etwas Speed. :-D

Deine Codeänderung bastel ich um (wir haben nämlich auch Nummern, die so aussehen: 001, 0010 usw. :evil: ) und teste das Ganze. Ich meld mich dann später, wenn ich mehr weiß.

Danke schon mal, die Idee sieht vielversprechend aus.

Bis später.

27. Juni 2007 12:38

Hi Michael,

danke für deine Hilfe, aber Untergrenze und Obergrenze müssen Text bleiben, da wir auch Artikelnummern haben, die so aussehen: 001, 0010, 0099 usw. :evil:

Das hatte ich erstmal verschwiegen, da es sonst zu komplex geworden wäre, um´s zu besprechen, sorry. :oops:

27. Juni 2007 12:43

Hi Dune,
das ist ja egal, denn du fängst ja mit 1000 an und durch das evaluate wird ja eine Integer draus, die du dann wieder mit Format in Text wandelst, das evaluate kann man sich sparen, wenn man gleich mit Integer die Schleife fährt.
Alternativ könntest du dir sämtliche wandlungen sparen und nur mit Text arbeiten, das Hochzählen machst du dann mit INCSTR(indexCode).
Aber ich denke, das ist langsamer.

27. Juni 2007 12:54

Ich weiß, aber Untergrenze und Obergrenze sollen später variabel bleiben und für alle möglichen Artikelnummern gültig sein. Später kann die Untergrenze auch '001' sein. Evaluate wird ja eh nur einmal vor der Schleife ausgeführt.

Ich denke, mein Evaluate hat nicht so gravierende Auswirkungen auf die Geschwindigkeit wie die Gets, oder?

27. Juni 2007 13:10

Das ist wohl richtig, aber immerhin....
Aber: Durch deine Konstruktion ist es z.B. nicht möglich den Artikel 001 zu finden, weil ein Format(1) eben '1' gibt und nicht '001'.
Dann besser direkt mit Codevariablen und INCSTR arbeiten, dann bist du wirklich flexibel, dann kannst du sogar mit solchen Konstrukten wie KFZ001, KFZ002 usw als Artikelnr. arbeiten.

27. Juni 2007 13:14

Michael Schumacher hat geschrieben:Dann besser direkt mit Codevariablen und INCSTR arbeiten, dann bist du wirklich flexibel, dann kannst du sogar mit solchen Konstrukten wie KFZ001, KFZ002 usw als Artikelnr. arbeiten.

Mein Vorschlag setzt ausschließlich nummerische Artikelnnummern voraus ... Schade, dass Dune den scheinbar nicht ausprobieren möchte - mich würde echt interessieren, ob die Performance dadurch besser oder schlechter wird.

28. Juni 2007 17:51

Hallo ihr beiden,

hatte jetzt Gelegenheit mir schon mal Gedanken zu euren Vorschlägen zu machen. Sorry, dass es länger gedauert hat, hatte noch andere Projekte.

Natalie´s Vorschlag, das Ganze mit Find zu lösen statt mit Get besitzt leider ein großes Problem:
Die Anzahl an Durchläufen ist abhängig von den existierenden Artikelnummern!
Sollten z.B. nur die Artikelnummern 1000 und 1002 existieren, dann würde man mit dieser Find-Schleife nur zwei Durchläufe bekommen und bei 1002 wäre Schluss, obwohl mir ja 1003, 1004 usw. auch zur Verfügung stehen.

Die Verknüpfung dieser Idee mit der Tabelle Ganzzahl könnte vielleicht zum Erfolg führen. Ich werd´s testen, kann mir aber nicht vorstellen, dass zwei verschachtelte Find schneller sind als Get. Besonders, weil Get die schnellste Variante sein soll (hab ich hier irgendwo im Forum gelesen).

Dank dir trotzdem , Natalie.

Hier mal ´ne andere Theorie:
Die Artikelnummern in eine temporäre Tabelle schreiben lassen und dann erst die Schleife mit Get auf diese temp. Tabelle zugreifen lassen, anstatt auf die "eigentliche" Artikeltabelle. Könnte ich vielleicht so Performance gewinnen, weil die Rechenarbeit jetzt auf dem Client stattfindet anstatt auf dem Server? Ist jetzt nur ´ne ungeteste Vermutung, könnte aber vielleicht funktionieren. Was meint ihr?

28. Juni 2007 18:31

Dune hat geschrieben:Die Anzahl an Durchläufen ist abhängig von den existierenden Artikelnummern!
Sollten z.B. nur die Artikelnummern 1000 und 1002 existieren, dann würde man mit dieser Find-Schleife nur zwei Durchläufe bekommen und bei 1002 wäre Schluss, obwohl mir ja 1003, 1004 usw. auch zur Verfügung stehen.


Dann hast du meinen Beitrag nicht genau gelesen ;-) (z.B. das Edit darunter)