3. August 2007 16:33

Hi Sandra!

Tja, wo anfangen ... pack' mer 's an ...

SQL mäßig entsteht so der Index
Sachkonto
Offen
Lfd. Nr.
= Eindeutig.
Jetzt habe ich einen 2ten Index angelegt, auf dem SQL Server
Sachkonto
Offen
nicht eindeutig.


Mit 4.00 ist es nicht mehr erforderlich den Index manuell auf SQL Seiter zu erstellen; hier genügt es die Eigenschaft "SQL Index" des entsprechenden Keys zu nutzen, in Deinem Fall also auf "Sachkonto","Offen". NAV ist inzwischen schlau genug in diesem Fall nicht das UNIQUE Flag zu setzten und nicht "Lfd Nr." anzufügen.

Punkt 1) er macht ja mehrere Abfragen daraus, aber ich glaube die erste ist die Ausschlaggebende => die habe ich mir gegriffen und dann mal direkt laufen lassen.

Raus kam folgende SQL Abfrage
SELECT * FROM "JWFIN_Heike"."dbo"."Jack Wolfskin$Sachposten"
WHERE (("Sachkontonr."='17722'))
AND (("Offen"=1))
ORDER BY "Sachkontonr."
OPTION (FAST 5)

Navision kommuniziert mit dem SQL Server größtenteils über sog. "parameterisierte Remote Procedure Calls". Das erste Ereignis - SP: StmtCompleted - ist der Aufbau des Statements wobei die WHERE Klausel durch Platzhalter @P1, etc. bestimmt wird.
Das zweite Ereignis - SP: RPCCompleted - ist die Ausführung des Statements aus 1. mit Übergabe der Parameter-Werte.
Erhält der SQL Server einen solchen Call das erste mal, so wird ein Ausführungsplan erstellt. Wird der gleiche Call erneut ausgeführt, wobei sich lediglich die Paramter-Werte geändert haben, so wird kein neuer Plan erstellt, sondern der alte wird weiterhin benutzt. Dieses Verhalten nennt man "Parameter Sniffing".
Ein Problem, das hierbei häufug mit NAV auftritt ist, daß bei ersten Aufbau des Calls oft "schlechte" Parameter übergeben werden (i.d.R. aus der GUI via ZUP Datei). Die Folge ist ein schlechter Ausführungsplan, der permanent wiederverwendet wird, auch wenn bei künftigen Calls die Parameter "besser" wären.
Dies ist oft der Grund dafür, daß das NAV Verhalten online anders ist als beim Reproduzieren der Abfrage als ad hoc Statement.

Also noch mal gleichen Select mit WITH (INDEX = $Mein Index)

Es gibt in NAV die Möglichkeit bei bestimmten Abfragen den INDEX Hint mitzugeben; dazu müssen entsprechende Eintrage in die Tabelle "$ndo$dbconfig" eingetragen werden (Details im "Application Designer's Guide"). Diese Maßnahme sollte aber nur benutzt werden, wenn alles andere versagt.
Mit SQL Server 2005 gibt es weiterhin die Möglichkeit, sog. "Plan Guides" zu erstellen. Auch hier kann man für spezifische Abfragen OPTIONS vorgeben, wie z.B. die Anweisung RECOMPILE um dem "Parameter Sniffing" Problem entgegen zu wirken.

Geschätzte CPU Kosten 3,2588 auf meinem Index + 0,001591 auf dem Clustered Index.

Die Kosten sind nicht unbedingt entscheidend. Wichtiger ist z.B. die Anzahl der Seiten die logisch gelesen werden müssen (SET STATISTICS IO ON) und der Ausführungsplan.

Wenn ich die gleiche Abfrage noch mal starte mit einem Konto für das kaum Einträge besteht, dann macht er es genau richtig!!!????

Tja, SQL Server prüft - via Statistiken - wie groß das zu erwartende Resultset einer Abfrage ist. Wenn also z.B. der Anteil der Datensätze für das entsprechende "Problemkonto" einen erheblichen Teil der Gesamtmenge einnimmt, wird sich SQL Server mit höherer Wahrscheinlichkeit für einen Clustered Index Scan entscheiden (hängt auch mit dem SELECT * zusammen).

Nun, das obige erklärt - hoffentlich - ein wenig das Problem. löst es aber u.U. nicht. Was die Sachposten-Tabelle angeht, so kann es z.B. Sinn machen - abhängig von der Abfragestruktur -, den Clustered Index auf Basis der "Sachkontonr." und des "Buchungsdatums" aufzubauen!

Ich hoffe Dir ein wenig geholfen zu haben ...

"JWFIN_Heike"."dbo"."Jack Wolfskin$Sachposten"

Wenn der Mandantenname Deinen Arbeitgeber wiedergibt, dann richte Herrn C. bitte einen schönen Gruß von mir aus!

Schöne Grüße,
Jörg

Re: Performance (Code Optimierung)

28. August 2009 09:31

Angeregt von diesem Beitrag möchte ich diesen Tipp "kund tun" bzw. diskutieren:
http://www.msdynamics.de/viewtopic.php?p=39010#p39010

Thema: Datensätze Gruppieren nach einem Feld
(Muss nicht im Key enthalten sein!)

Oft wird dieser Code verwendet:
Code:
// Rec = Tabelle 25
// LKreditor = Tabelle 23, Temporary = Yes
IF FINDSET THEN BEGIN
  REPEAT
    LKreditor.SETRANGE(LKreditor."Nr.","Kreditorennr.");
    IF LKreditor.ISEMPTY THEN BEGIN
      LKreditor."Nr." := "Kreditorennr.";
      LKreditor.INSERT;
    END;
  UNTIL NEXT=0;
  LKreditor.SETRANGE(LKreditor."Nr.");
END;


Dieser Code ist in meinem Test um Faktor 35 schneller (Statt 120 Sekunden nur 3,3 :!: ).
(In einem anderen Test um Faktor 25)

Code:
IF FINDSET THEN BEGIN
  REPEAT
    LKreditor."Nr." := "Kreditorennr.";
    IF LKreditor.INSERT THEN;
  UNTIL NEXT=0;
END;


Meine Frage z.B. wäre ist dieses auf einem SQL-Server Ok aus Sicht der CPU-Cost oder Server-Cost (ich weiß nicht genau wie das heißt!)

Re: Performance (Code Optimierung)

28. August 2009 10:04

mikka hat geschrieben:Meine Frage z.B. wäre ist dieses auf einem SQL-Server Ok aus Sicht der CPU-Cost oder Server-Cost (ich weiß nicht genau wie das heißt!)
Da temporär definierte Record-Variablen im Arbeitsspeicher des Clients verwaltet werden, bekommt der SQL-Server davon nichts mit.
Seine einzige Aufgabe ist es, alle real existierenden Datensätze auszuliefern.

Dein Trick klingt nicht schlecht, funktioniert aber nur dann, wenn das Feld - aufgrund der Länge - auch in den Primärschlüssel übertragen werden kann.
In dem von dir zitierten Beispiel funktioniert der Trick z. B. nicht, da das entscheidende Feld deutlich länger sein kann, als das Primärschlüsselfeld.
Abgesehen davon steht dir dann der wahre Inhalt des Primärschlüsselfeldes nicht mehr zur Verfügung. Dies muss man situationsbedingt betrachten.

Re: Performance

28. August 2009 15:03

Das mit dem Prim.Key ist mir bewust nicht aufgefallen, wir arbeiten bei uns immer mit Arbeitsdateien, die alle möglichen Varianten von Feldtypen (und Längen) beeinhalten.
In meinem Beispiel habe ich extra die Kreditortabelle gewählt, falls jemand es ausprobieren möchte.

Tima Lässer hat geschrieben:Da temporär definierte Record-Variablen im Arbeitsspeicher des Clients verwaltet werden, bekommt der SQL-Server davon nichts mit.
Seine einzige Aufgabe ist es, alle real existierenden Datensätze auszuliefern.

Stimmt, ich habe es gerade zu "spüren" bekommen, ich lasse gerade die Tabelle ungefiltert verarbeiten.
(ca. 350.000 Datensätze (DS) mit 10.000 Kreditoren)
Mit optimierten Code waren es 97 Sekunden, mit dem alten vorraussichtlich 1 Stunde (53 Minuten). Folge: -->Mein PC ist total lam..schig.
***Nachtrag: Ich habe die Session gekillt, hat ewig geaduert****

-->Ich glaube das macht die Bedeutung von optimierten Code ziemlich ersichtlich.

Was mich stutzig macht, ist allerdings die Zeit pro DS. Da komme ich auf 0,277 Milli-Sekunden. Den Wert glaube ich nicht.
(Es sei den, der Server hat alles im Cache?!)

Re: Performance

28. August 2009 16:53

Bei nicht-temporären Tabellen sollte IF ... INSERT THEN; beim SQL-Server aber vermieden werden, dazu fällt mir dieser alte Thread bei Mibuso wieder ein. :-)