[Gelöst] REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

17. August 2009 14:19

Meine Frage ist an keine NAV Version gebunden, deswegen bitte verschiebene falls es hier unpassend ist.

Code:
Cust.SETCURRENTKEY("Currency Code"); //damit alle Customer mit Currency Code='EUR' an einem Stück stehen
Cust.SETRANGE("Currency Code",'EUR');
Cust.MODIFYALL("Currency Code",'',TRUE);


Mir geht es darum wie "Sprünge" in der Sortierung passieren können, wenn ich statt MODIFYALL folgenden Code nutze:

Code:
Cust.SETCURRENTKEY("Currency Code");
Cust.SETRANGE("Currency Code",'EUR');
IF Cust.FIND('-') THEN
REPEAT
Cust."Currency Code." := '';
UNTIL Cust.NEXT = 0


Unser Trainer beim Developer I (impuls) hat gesagt, dass hier die Zeilen aus der Abgrenzung "rutschen" können und dadurch Fehler entstehen können. Wie das passieren kann ist mir nicht ganz klar. An dieser Stelle hat er auch erwähnt, dass NAV keine Cursor kennt, wie andere Datenbanken. NAV schaut sich stehts zuerst den Currentkey an und danach den PK um "von einem Datensatz zum nächsten" zu kommen. Aber eigentlich müsste letzterer Code doch gut funktionieren, da das Rausrutschen aus dem Filter ja kein Problem darstellt oder?
Zuletzt geändert von dayscott am 20. August 2009 12:31, insgesamt 1-mal geändert.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

17. August 2009 14:47

dayscott hat geschrieben:
Code:
Cust.SETCURRENTKEY("Currency Code"); //damit alle Customer mit Currency Code='EUR' an einem Stück stehen
Cust.SETRANGE("Currency Code",'EUR');
Cust.MODIFYALL("Currency Code",'',TRUE);
Hier besteht bei einigen NAV-Versionen die Gefahr, dass der OnModify-Trigger (entgegen dem gesetzten Parameter) nicht durchlaufen wird.

dayscott hat geschrieben:
Code:
Cust.SETCURRENTKEY("Currency Code");
Cust.SETRANGE("Currency Code",'EUR');
IF Cust.FIND('-') THEN
REPEAT
  Cust."Currency Code." := '';
UNTIL Cust.NEXT = 0
Aber eigentlich müsste letzterer Code doch gut funktionieren, da das Rausrutschen aus dem Filter ja kein Problem darstellt oder?
Immer dann, wenn du die Felder änderst, nach welchen du auch sortiert hast, besteht die Gefahr, dass
  1. es zu einer Endlosschleife kommt
  2. direkt nach dem ersten bearbeiteten Datensatz Schluß ist, da der "Cursor" an das Ende gerutscht ist
Die einzige saubere und sichere Lösung, welche mir bekannt ist, währe, dass du die tatsächliche Datenänderung auf einer Record-Kopie durchführst:
Code:
Cust.SETCURRENTKEY("Currency Code");
Cust.SETRANGE("Currency Code",'EUR');
IF Cust.FIND('-') THEN
REPEAT
  Cust2 := Cust;
  Cust2.VALIDATE("Currency Code.",'');
  Cust2.MODIFY(TRUE);
UNTIL Cust.NEXT = 0
Kleine Anmerkung: Man sollte immer mit VALIDATE und MODIFY(TRUE) arbeiten, es sei denn, man weiß ganz genau, was man macht und welche Auswirkungen es hat.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

17. August 2009 14:55

Timo Lässer hat geschrieben:Hier besteht bei einigen NAV-Versionen die Gefahr, dass der OnValidate-Trigger (entgegen dem gesetzten Parameter) nicht durchlaufen wird.

Du meintest sicherlich den OnModify-Trigger?

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

17. August 2009 14:59

Natalie hat geschrieben:
Timo Lässer hat geschrieben:Hier besteht bei einigen NAV-Versionen die Gefahr, dass der OnValidate-Trigger (entgegen dem gesetzten Parameter) nicht durchlaufen wird.

Du meintest sicherlich den OnModify-Trigger?

:oops: Ähh, ja - sorry :roll:
Habe es korrigiert.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

17. August 2009 15:42

Timo Lässer hat geschrieben:
Code:
Cust.SETCURRENTKEY("Currency Code");
Cust.SETRANGE("Currency Code",'EUR');
IF Cust.FIND('-') THEN
REPEAT
  Cust2 := Cust;
  Cust2.VALIDATE("Currency Code.",'');
  Cust2.MODIFY(TRUE);
UNTIL Cust.NEXT = 0


Hinzu kommt, das dieses der bessere Code (aus Performace Sicht )für einen SQL-Server ist. (Über das FIND('-') bzw. FINDSET gibt es ja nach wie vor Uneinigkeit!)

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

17. August 2009 15:53

mikka hat geschrieben:[...](Über das FIND('-') bzw. FINDSET gibt es ja nach wie vor Uneinigkeit!)

dayscott hat geschrieben:Meine Frage ist an keine NAV Version gebunden, [...]

Aus diesem Grund habe ich das FIND('-') mal so stehen gelassen.
Klar, ab NAV 4.0 SP1 auf SQL-Server sollte man aus Performance Gründen FINDSET(TRUE) verwenden.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

17. August 2009 16:22

Timo Lässer hat geschrieben:
dayscott hat geschrieben:
Code:
Cust.SETCURRENTKEY("Currency Code"); //damit alle Customer mit Currency Code='EUR' an einem Stück stehen
Cust.SETRANGE("Currency Code",'EUR');
Cust.MODIFYALL("Currency Code",'',TRUE);
Hier besteht bei einigen NAV-Versionen die Gefahr, dass der OnModify-Trigger (entgegen dem gesetzten Parameter) nicht durchlaufen wird.
.

super Sache,... wieso wird dann in der Schulung genau das als Best Practise tituliert? -.-

Timo Lässer hat geschrieben:Immer dann, wenn du die Felder änderst, nach welchen du auch sortiert hast, besteht die Gefahr, dass
es zu einer Endlosschleife kommt
direkt nach dem ersten bearbeiteten Datensatz Schluß ist, da der "Cursor" an das Ende gerutscht ist


Kann man näher begründen warum und wann genau diese unerwünschten Seiteneffekte passieren, falls sie denn passieren?

Timo Lässer hat geschrieben:Die einzige saubere und sichere Lösung, welche mir bekannt ist, währe, dass du die tatsächliche Datenänderung auf einer Record-Kopie durchführst:
Code:
Cust.SETCURRENTKEY("Currency Code");
Cust.SETRANGE("Currency Code",'EUR');
IF Cust.FIND('-') THEN
REPEAT
  Cust2 := Cust;
  Cust2.VALIDATE("Currency Code.",'');
  Cust2.MODIFY(TRUE);
UNTIL Cust.NEXT = 0



Das ist dann das Best Practise für die Praxis?

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

17. August 2009 19:49

dayscott hat geschrieben:
Timo Lässer hat geschrieben:
dayscott hat geschrieben:
Code:
Cust.SETCURRENTKEY("Currency Code"); //damit alle Customer mit Currency Code='EUR' an einem Stück stehen
Cust.SETRANGE("Currency Code",'EUR');
Cust.MODIFYALL("Currency Code",'',TRUE);
Hier besteht bei einigen NAV-Versionen die Gefahr, dass der OnModify-Trigger (entgegen dem gesetzten Parameter) nicht durchlaufen wird.
super Sache,... wieso wird dann in der Schulung genau das als Best Practise tituliert? -.-
Weil der Befehl MODIFYALL genau für diese Aufgaben vorgesehen ist.
Sofern man ohne den OnModify-Trigger auskommt (situationsabhängig!!!), ist es auch in der Praxis die eleganteste Art.
Leider gibt es jedoch NAV-Versionen, bei denen die Ausführung des OnModify-Triggers nicht angestossen wird (leider weiß ich nicht auswendig, welche Versionen dies konkret sind).

dayscott hat geschrieben:
Timo Lässer hat geschrieben:Immer dann, wenn du die Felder änderst, nach welchen du auch sortiert hast, besteht die Gefahr, dass
es zu einer Endlosschleife kommt
direkt nach dem ersten bearbeiteten Datensatz Schluß ist, da der "Cursor" an das Ende gerutscht ist
Kann man näher begründen warum und wann genau diese unerwünschten Seiteneffekte passieren, falls sie denn passieren?
Jetzt wird es kompliziert...
Nehmen wir das Beispiel mit dem Währungscode:
Du sortierst danach, somit stehen die Datensätze nach Währungscode aufsteigend zur Verfügung.
Änderst du nun im ersten Datensatz den Währungscode von EUR nach USD, so rutscht dieser Datensatz in der Sortierung weiter nach hinten.
(Wenn du gefiltert hast, dann sogar ausserhalb des Filters.)
Somit gibt es in der Sortierung keinen folgenden Datensatz mit Währungscode EUR, da wir mittlerweile bei USD angekommen sind.

dayscott hat geschrieben:
Timo Lässer hat geschrieben:Die einzige saubere und sichere Lösung, welche mir bekannt ist, währe, dass du die tatsächliche Datenänderung auf einer Record-Kopie durchführst:
Code:
Cust.SETCURRENTKEY("Currency Code");
Cust.SETRANGE("Currency Code",'EUR');
IF Cust.FIND('-') THEN
REPEAT
  Cust2 := Cust;
  Cust2.VALIDATE("Currency Code.",'');
  Cust2.MODIFY(TRUE);
UNTIL Cust.NEXT = 0
Das ist dann das Best Practise für die Praxis?
Wie ich schon weiter oben in diesem Beitrag geschrieben habe:
Ja, wenn du den OnModify-Trigger ausführen lassen willst.
Muss der OnModify-Trigger nicht ausgeführt werden, so kannst du bedenkenlos MODIFYALL verwenden.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

17. August 2009 20:52

Die Nutzung von MODIFYALL() oder Loop-MODIFY() sollte nicht von einem Fehler in einer Version abhängig gemacht werden, sondern von der in der Dokumentation beschrieben Funktion dieser Befehle. Wenn es eine Version von NAV mit einem solchen Fehler gibt (entzieht sich derzeit meiner Kenntnis), dann MUSS diese auf jeden Fall ausgetauscht werden und darf in keinster Weise Einfluss auf die Programmierung haben!!

Die einzige Begründung die für die Entscheidung ob nun Loop-MODIFY() oder MODIFYALL() genutzt wird, sollte das Ergebnis der folgenden Tests und die Menge an Daten sein, die im speziellen oder allgemein in einem solchen Vorgang bearbeitet wird: What impact does my C/AL have on SQL - MODIFYALL/DELETEALL.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

18. August 2009 11:14

SilverX hat geschrieben:Die Nutzung von MODIFYALL() oder Loop-MODIFY() sollte nicht von einem Fehler in einer Version abhängig gemacht werden, sondern von der in der Dokumentation beschrieben Funktion dieser Befehle. Wenn es eine Version von NAV mit einem solchen Fehler gibt (...) dann MUSS diese auf jeden Fall ausgetauscht werden und darf in keinster Weise Einfluss auf die Programmierung haben!!


Was soll ausgetauscht werden? Also hats doch Einfluss auf die Programmierung ?

@Timo
Änderst du nun im ersten Datensatz den Währungscode von EUR nach USD, so rutscht dieser Datensatz in der Sortierung weiter nach hinten.
(Wenn du gefiltert hast, dann sogar ausserhalb des Filters.)
Somit gibt es in der Sortierung keinen folgenden Datensatz mit Währungscode EUR, da wir mittlerweile bei USD angekommen sind.


Es gibt ja schon noch zahlreiche Records mit Währungscode EUR, und mein SETRANGE ist doch immernoch gültig, wieso stehe ich also auf einmal in USD? In NAV Tabellen gibts doch keine Cursor ! : /
Mein Problem ist wohl das ich den Befehl .NEXT nicht ganz durschaut habe, die Online Hilfe hat mir auch nicht weitergeholfen.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

18. August 2009 17:10

dayscott hat geschrieben:Was soll ausgetauscht werden? Also hats doch Einfluss auf die Programmierung ?


Nein, es sollte keinen Einfluss haben! Ich meinte folgendes: MODIFYALL() ist folgendermaßen definiert:

Record.MODIFYALL(Field, NewValue [, RunTrigger])

Was bedeutet, dass beim Setzen des dritten Parameters auf TRUE der OnModify() Trigger ausgeführt wird! Wenn das aufgrund eines Fehlers in der fin.exe oder finsql.exe nicht passiert, dann ist es nicht sinnvoll, deswegen MODIFY zu verwenden (um einen Fehler des Programms zu umgehen), sondern es sollte auf jeden Fall ein entsprechender NAV Hotfix oder eine neue Version eingespielt werden, so dass die Applikation wie in der Dokumentation beschrieben funktioniert.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

18. August 2009 17:39

Ein Beispiel für die Fehlfunktion bei Nutzung der gleichen Record Instanz bei Änderungen. Nimm folgenden Code an, wobei Debitor 10000 und 20000 auf EUR gesetzt wurden. Der Ersatzwert WST ist bisher in keinem Debitor verwendet:

Code:
Customer.SETCURRENTKEY("Currency Code");
Customer.SETRANGE("Currency Code", 'EUR');

IF Customer.FIND('-') THEN REPEAT
 
  Customer."Currency Code" := 'WST';
  Customer.MODIFY;

UNTIL Customer.NEXT = 0;


Erster Durchlauf: 10000, EUR -> WST. Da der Wert des aktuellen Schlüssels "Currency Code" erst EUR, nach Änderung dann WST. Aktueller Datensatz ist damit dann auch WST, also außerhalb von EUR. Damit kann als nächster Datensatz (NEXT) kein EUR mehr kommen und die Schleife wird nach dem ersten Durchlauf bereits verlassen ohne den zweiten EUR Datensatz anzufassen.

Würde der aktuelle Schlüssel der Primärschlüssel sein, dann hättest du keine Probleme, da der PK sich nicht verändert sondern nur der Währungscode. Sobald du aber per SETCURRENTKEY einen Schlüssel setzt deren Feldwert(e) du veränderst, kommt NAV spätestens beim nächsten NEXT aus der Spur.

Code:
Customer.SETCURRENTKEY("Currency Code");
Customer.SETRANGE("Currency Code", 'EUR');

WHILE Customer.FIND('-') DO BEGIN
  Customer."Currency Code" := 'WST';
  Customer.MODIFY;
END;

Obiger Code beispielsweise funktioniert super, allerdings ist er schon sehr langsam. Hier wird immer ein neuer Datensatz gelesen der dem Filter entspricht ohne den Datensatzzeiger weiter zu bewegen. Also immer der erste zutreffende.

Ich hoffe das ist einigermaßen verständlich...

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

19. August 2009 09:22

SilverX hat geschrieben:1. Würde der aktuelle Schlüssel der Primärschlüssel sein , dann hättest du keine Probleme , da der PK sich nicht verändert sondern nur der Währungscode.Sobald du aber per SETCURRENTKEY einen Schlüssel setzt deren Feldwert(e) du veränderst, kommt NAV spätestens beim nächsten NEXT aus der Spur.
....
2. Obiger Code beispielsweise funktioniert super, allerdings ist er schon sehr langsam. Hier wird immer ein neuer Datensatz gelesen der dem Filter entspricht ohne den Datensatzzeiger weiter zu bewegen.


1. Also ich stell mir die Datensätze bildlich vor: zb beim Ordnen nach Währungscode stehen Euros im Block, die Wst im Block etc. Die Blöcke werden alphabetisch sortiert. Die Sortierung innerhalb der Blöcke erfolg nach PK.

Das Gedankenspiel: "wenn du nach PK sortieren würdest" ergibt doch keinen Sinn . Wie will ich die Records iterativ durchgehen und alle Euros durch Wst ersetzen wenn meine Records nach PK (stellen wir uns hierführ eine eindeutige ID vor) sortiert sind?

2. Es gibt keine "Datensatzzeiger" in NAV hat unser Trainer gesagt X (

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

19. August 2009 09:28

Natürlich "gibt das Sinn", denn ohne den SETCURRENTKEY("Currency Code") läuft dieses Beispiel Problemlos.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

19. August 2009 13:12

ouh - Noob-Fehler, hast natürlich recht.

NAV braucht nur sehr lange (für den nachstehenden Code) weil SETRANGE auf dem Server lange braucht um "realisiert" zu werden. D.h. die Suchtransaktion(FIND('-')) [bzw was ist der Oberbegriff für FIND/GET...?] wird durch die ungünstige Wahl des Schlüssels einfach lahm - richtig?

Code:
//Customer.SETCURRENTKEY("Currency Code");
Customer.SETRANGE("Currency Code", 'EUR');
IF Customer.FIND('-') THEN REPEAT // braucht sehr lange weil das SETRANGE auf dem Server sehr lange "ausgeführt" wird.
 
  Customer."Currency Code" := 'WST';
  Customer.MODIFY;

UNTIL Customer.NEXT = 0

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

19. August 2009 13:44

dayscott hat geschrieben:NAV braucht nur sehr lange [...] weil SETRANGE auf dem Server lange braucht um "realisiert" zu werden. D.h. die Suchtransaktion(FIND('-')) [...] wird durch die ungünstige Wahl des Schlüssels einfach lahm - richtig?
Richtig, deshalb ruhig den optimalen Schlüssel für den Filter wählen und die tatsächliche Modifikation auf einer Record-Kopie durchführen.
Somit hast du die beste Performance und deine Sortierung kommt nicht durcheinander.

Re: REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

19. August 2009 14:34

Hallo dayscott,

das größte Problem bei deiner Aktion ist, das du das Feld ändern willst auf das du filterst. Wer schon mal in NAV mit STRG-H (Suchen u. Ersetzen) ein gefiltertes Feld bearbeitet hat, wird wissen das i.d.R. nur jedes dritte Feld umbenannt wird :!: :?: (zumindest in der Native-DB).
Deshalb kann ich nur Timos Lösung mit dem 2. Record empfehlen, da diese Lösung auch jeden Record erwischt.

Gruß, Fiddi

Re: [Gelöst] REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

14. September 2009 16:01

Cust.SETCURRENTKEY("Currency Code");
Cust.SETRANGE("Currency Code",'EUR');
IF Cust.FIND('-') THEN
REPEAT
Cust2 := Cust;
Cust2.VALIDATE("Currency Code.",'');
Cust2.MODIFY(TRUE);
UNTIL Cust.NEXT = 0

Hierzu mal eine Frage: der Originaldatensatz wird ja nur auf den zweiten Tablehandle kopiert, wahrscheinlich (hier fehlt mir leider viel Hintergrundwissen) mit allen internen Zeigern usw. Besteht dann nicht die Gefahr, daß der Originaldatensatz überschrieben wird, auch wenn man die Kopie speichert und man somit trotzdem in das Problem läuft, das eigentlich umgangen werden soll ?

Bisher hatte ich sowas immer damit gelöst, daß ich über den Primarykey eine Kopie geladen habe (Cust2.GET(Cust.<PrimaryKey>)). Das ist dann aber wohl, aus Performancesicht, ungünstig ?!

Re: [Gelöst] REPEAT..UNTIL Schleife statt MODIFYALL - Risiken?

14. September 2009 16:26

whynot hat geschrieben:Hierzu mal eine Frage: der Originaldatensatz wird ja nur auf den zweiten Tablehandle kopiert, wahrscheinlich (hier fehlt mir leider viel Hintergrundwissen) mit allen internen Zeigern usw. Besteht dann nicht die Gefahr, daß der Originaldatensatz überschrieben wird, auch wenn man die Kopie speichert und man somit trotzdem in das Problem läuft, das eigentlich umgangen werden soll ?

Bisher hatte ich sowas immer damit gelöst, daß ich über den Primarykey eine Kopie geladen habe (Cust2.GET(Cust.<PrimaryKey>)). Das ist dann aber wohl, aus Performancesicht, ungünstig ?!


Zunächst ein "Herzlich Willkommen" auch an dich :-)

Zur ersten Frage:
Dieses Konstrukt soll den Linearen Zugriff (ich nenne es mal so) nicht zerstören, da dieses Performaceeinbrüche mit sich bringen kann.
Da es auf den Datensatz des ersten Tablehandle kein MODIFY gibt, wird dieser auch nicht verändert.
-->Oder habe ich Deine Frage Missverstanden?

Zur zweiten:
Ich sehe kein Problem darin, ein GET zu benutzen. Ist doch eine der schnellsten Varianten einen DS zu holen, um diesen (einzelnen) zu editieren .