SQL-Server und Befehl COUNT

18. Juni 2007 16:45

Hallo zusammen,

ich hab mal ne allgemeine Frage bezüglich des SQL-Servers und dem Befehl COUNT (hoffe ich bin ihm richtigen Forum, ansonsten bitte verschieben)

Wir haben in unserem System den Report 1001 der über mehrere DataItems geht (Item -> Item Ledger Entry -> Value Entry). Dadurch, dass 2 Postentabellen durchlaufen werden, müssen natürlich viele Datensätze aufgerufen werden, dementsprechend läuft der Bericht bei uns ca. 1 Stunde für den Zeitraum von einem Monat. Deshalb habe ich einen Fortschrittsbalken nach dem Schema von Navision24 eingebaut.
Wir setzen eine native Datenbank ein und die Laufzeit hat sich nicht wesentlich verändert. Vor kurzem ist allerdings ein Fehler aufgetreten, weswegen uns NSC den Bericht mal getestet hat. Hier lief er über 18 Std. (bzw. über 6 mit anderer Filterung) was natürlich deutlich länger als bei uns ist.
Meine Frage, liegt dieser krasse Performanceeinbruch am SQL-Server der von unserem NSC eingesetzt wird in Verbindung mit dem Fortschrittsbalken? Für den Fortschrittsbalken wird ja der Befehl COUNT des öfteren verwendet, aber ich finde es trotzdem komisch, dass sich der Bericht dadurch so sehr verlangsamt...

Würde mich (auch im Hinblick auf eine geplante Umstellung auf SQL) sehr interessieren, ob dem so ist und wie das vermieden werden kann.

Danke schonmal und Grüße
Alez

18. Juni 2007 17:38

Ich habe diesen Beitrag von "Dynamics NAV Tipps & Tricks" nach "Dynamics NAV 4.xx" verschoben.

Gruß, Marc

18. Juni 2007 19:54

Hi!

Erst mal grundsätzlich zu COUNT und COUNTAPPROX mit SQL Server:

COUNT wird zu einem SELECT COUNT(*), also ein echtes und genaues Zählen der Datensätze in der Tabelle. Je nach Größe der Tabelle und des verwendeten Indexes(!) kann dies ein Weilchen dauern (wir reden hier normalerweise über Sekunden).

Mit COUNTAPPROX wird nur der SHOWPLAN_ALL für einen SELECT * ausgewertet und die Anzahl der Datensätze dem Teil "EstimateRows" entnommen.
Da hier keine echte Abfrage ausgeführt wird - also kein Resultset zurückgegeben wird - ist dies bedeutend schneller, ist u.U. aber ungenau (deswegen ja Estimate).

Also: Präzision versus Performance.

Für Statusbalken sollte es i.d.R. der COUNTAPPROX tun, allerdings tut ein COUNT auch nicht so richtig weh, je nach dem wie oft und mit welchem Index(!) gezählt wird.

Wie so oft: "Es hängt davon ab ..."

P.S: Die Performance-Unteschiede zwischen nativ und SQL Server liegen nicht allein beim COUNT, bedauerlicherweise kommt hier noch so einiges hinzu ... das Forum is voll mit Hinweisen, Erklärungen, Tips & Tricks diesbezüglich!

19. Juni 2007 07:15

Ein weiterer Punkt dazu betreffend NAV:

Das "malen" von Fortschrittskisten frisst im allgemeinen SEHR viel Performance. Sicher keine 17 Stunden, aber du kannst durchaus bei langen Verarbeitungen bis zu 20-25% verschenken.

Fortschrittsbalken bei längeren Verarbeitungen löse ich immer folgendermaßen:

Code:
IF Count MOD 250 = 0 THEN BEGIN
  Dialog.UPDATE(...);
END;


Die 250 muss natürlich je nach Gegebenheit und erforderlicher Genauigkeit angepasst werden.

@Jörg: Hoffe du bist gut angekommen. Nachts ist die Oberleitung der Strecke gerissen, hattest also Glück mit dem Zug um 20:00 Uhr ;-)

19. Juni 2007 07:35

Da stimme ich mit dir überein: Die Fortschrittsanzeige ist sehr performancefressend.
Da der COUNT es aber auch ist, ermittel ich die Anzahl Datensätze nur ein einziges mal am Anfang und merke mir das Ergebnis in einer Variable.

Ob man den Dialog nun alle 250 Datensätze aktualisiert oder erst alle 1.000 hängt davon ab, wieviele Datensätze ich zu verarbeiten habe.
Wenn ich nur 1.000 Zeilen verarbeite, dann kann man auch alle 25 Sätze ein Update machen, sind es jedoch 100.000 oder mehr, dann darf es auch mal alle 1.000 Sätze (oder später) ein Dialog.Update absetzen.
Code:
IF NoOfRecs MOD 1000 = 0 THEN
  Dialog.UPDATE(...);

19. Juni 2007 07:46

@Carsten: Danke der Nachfrage. Ich bin problemlos & pünktlich Zuhause angekommen :wink:

19. Juni 2007 09:05

Guten Morgen zusammen,

danke erstmal für die vielen Antworten. Noch als allgemeiner Hinweis, ich weiß dass es schon einen Thread gibt, der sich mit der allgemeinen Perfomance befasst, habe aber hier die Frage gestellt, weil beim unserem NSC die Verarbeitung so stark verlängert wurde...


@ Stryk
Danke für den Hinweis mit COUNTAPPROX, ich denke für etwas einfaches wie den Statusbalken dürfte dieser Befehl wie du schon sagtest ausreichen.

@SilverX und Timo Lässer
was meint ihr mit "malen"? Die einfache Anzeige des Balkens? Heißt das, wenn ich z.B. nur die Prozentzahlen anzeigen würde, wäre der Report nicht so "langsam"? Wie gesagt, auf unserer nativen Datenbank merk man fast keinen Unterschied, max. ein paar Sekunden bei aktiviertem Fortschrittsbalken. Das mit den Pausen zwischen der Aktualisierung werde ich mir noch überlegen.

Den Count-Befehl verwende ich nur am Anfang 2 Mal pro Tabelle, eben wie in der Vorlage von Navision24.

Gruß
Alez

PS: Was macht denn der Befehl MOD? Hab in der Hilfe grad nichts gefunden.

19. Juni 2007 13:09

MOD ist die Abkürzung für Modulo (Division mit Rest)

siehe hier: http://de.wikipedia.org/wiki/Modulo

Das heißt also nur das aller 1000 Durchgänge der darunter liegende Befehl ausgeführt wird

Beispiel: 250 MOD 1000 = 0 Rest 250

Gruß Schaarschi

22. Juni 2007 13:34

Ob man den Dialog nun alle 250 Datensätze aktualisiert oder erst alle 1.000 hängt davon ab, wieviele Datensätze ich zu verarbeiten habe.

Da ja häufig nicht bekannt ist, wieviele Datensätze tatsächlich verarbeitet werden, und ich mir darüber keine weiteren Gedanken machen möchte, verwende ich meist folgendes Konstrukt:

Code:
DCounter += 1;
IF DCounter MOD ROUND(DCounterAll/DNumUpdates + 1, 1) = 0 THEN BEGIN
   D.UPDATE(...);
   ...
END;

Der Vorteil: Die Anzahl Schritte, wann die nächste Aktualisierung des Dialogs vorgenommen wird, ist nicht mehr hartkodiert, sondern wird dynamisch auf Basis der Anzahl zu verarbeitender Datensätze (DCounterAll) bestimmt. DCounter ist hier der Zähler und DNumUpdates gibt die maximale Anzahl Aktualisierungen in einem Lauf an.

Ein Rechenbeispiel für DNumUpdates = 100:

DCounterAll --> Anzahl Aktualisierungen
1.000.000 --> 100
10.000 --> 99
1.000 --> 83
100 --> 50
10 --> 9

Gruß,
Jens

22. Juni 2007 19:48

@j.r,
Du könntest noch ne Menge Zeit sparen, wenn du vor dem Durchlauf nach der Ermittlung der Gesamtzahl den Roundbefehl in eine Variable packst und dann nur noch mit der Variablen arbeitest, das spart pro Schleife 2 Berechnungen, also so:

Code:
DDivisor:=ROUND(DCounterAll/DNumUpdates + 1, 1)
REPEAT
  DCounter += 1;
  IF DCounter MOD DDivisor = 0 THEN BEGIN
    D.UPDATE(...);
    ...
  END;
  (Die restlichen Schleifenanweisungen ...)
UNTIL WatAuchImmerZumEndeFührt

25. Juni 2007 15:44

Das klingt ja schon mal interessant es dynamisch zu machen. Ich versteh bloß gerade noch nicht, wie DNumUpdates berechnet wird. Steh da grad ein bisschen auf dem Schlauch....

Gruß
Alez

25. Juni 2007 17:46

DNumUpdates ist die Anzahl der Updates die du für das Statusfenster brauchst. Bei 0-100% macht hier 100 Sinn ;-)

Ggf. Ist das aber zu wenig, weil du dann ggf. zwischendrin immer den altbekannten weißen Bildschirm hast. Dann könntest du, auch wieder je nach erwarteter Datenmenge, auch höher gehen.

26. Juni 2007 13:20

@Michael: Mit der Divisor-Variablen hast Du sicherlich Recht!

@Alez: Den Wert für DNumUpdates braucht man nicht unbedingt zu berechnen. Das ist lediglich die Anzahl der maximal von Dir gewünschten Updates.

Man könnte aber wie folgt vorgehen: Ich erwarte eine maximale Anzahl von 20.000 Datensätzen. Während der Verarbeitung möchte ich ungefähr alle 100 Schritte informiert werden. Dann verwende ich als DNumUpdates=20.000/100=200. Das führt zu 198 Aktualisierungen bei 20.000 Datensätzen bzw. zu 50 Aktualisierungen bei 100 Datensätzen.

Wenn ich nun nach der üblichen Methode "eine Aktualisierung alle x Schritte" vorgehe, dann mag bei 20.000 Datensätzen eine Schrittweit von x=200 richtig sein. Habe ich aber nur 100 Datensätze zu verarbeiten, dann bekomme ich mit der eingestellten Schrittweite überhaupt keinen Fortschritt angezeigt. Das umgehe ich einfach damit. Kein Muss, aber schön :-)

Gruß,
Jens

27. Juni 2007 10:42

Hallo,

also ist DNumUpdates ein Wert den ich im Report vorgebe und ich muss dann nur vorher abschätzen, wie viele Datensätze ich ungefähr erwarte? Ich werde das auf jeden Fall mal testen.

Ich versteh zwar jetzt immer noch nicht, wie du auf die Anzahl der Aktualisierung kommst, aber solange es funktioniert ist das ja noch in Ordnung ;-)

Gruß
Alez

27. Juni 2007 10:55

@Alez,

durch die Festlegung von DNumUpdates kannst du dir das Abschätzen sparen, das erledigt das Programm für dich.

Versuchen wir es mal so rum:
Du könntest z.B. einen Fortschrittsbalken einbauen.
Der Balken zeigt ja auch den Prozentwert des Fortschritts.
Wenn Du diesen Balken nun bei Erreichen eines jeden Prozents aktualisieren möchtest, setze DNumUpdates:=100.
Der Balken kann maximal 9999 unterschiedliche Werte plus der 0 anzeigen (also 0,01% Schritte). Die maximal sinnvolle Größe für den DNumUpdates bei einem Balken ist also 10000.
willst du nur alle 10% aktualisieren, dann ist DNumUpdates:=10.

27. Juni 2007 13:16

Ok, so versteh ich das :-) Danke für die Erläuterung.

Gruß
Alez