Seite 1 von 1

Queryergebnisse (schnell) durchlaufen

Verfasst: 25. September 2013, 09:00
von Magic
Hi,

ich arbeite mich zwar erst gerade in WinDev ein und habe schon das erste Projekt vorliegen mit dem Ziel Performanceoptimierung.
Dank Trace() und Zeitmessung habe ich die Engstellen im Projekt identifiziert, kann sie aber (noch) nicht beseitigen.
Eine diese Engstelle ist das Durchlaufen eines Querys.

Mit HExecuteQuery(...) werden Daten gelesen. Diese Abfrage habe ich schon soweit optimiert, dass nur noch Sekundenbruchteile nötig sind um das Ergebnis zu haben.
Dann wird der/die/das (was ist denn hier eigentlich richtig?) durchlaufen um Daten in einem Scheduler control anzuzeigen.

Ursprünglich wurde das Query mit der Anweisung:

Code: Alles auswählen

for ALL QRY_Abwesenheit
   ...
end
durchlaufen. Und obwohl die Anzahl der Datensätze gering ist (auch mal nur ein) Dauert es unheimlich lange bis das Ende erreicht ist.

Nun hatte ich probiert das Query über eine FOR Schliefe zu durchlaufen. Dafür brauche ich aber die Anzahl der Datensätze im Query. Mit Hilfe der Funktion HNbRec(...) und als Alternative auch mal mit HLast(...) und HRecNum(...) habe ich dann die Anzahl ermitteln wollen. Ergebnis alle "H"-Funktionen die ich auf das Query ausführe, dauern unheimlich lange. Unzumutbar lange.

Ich habe auch schon unterschiedliche Variationen mit LOOP und WHILE probiert. Die Schleifen sind dann nicht das Problem, aber dann z.B. das Lesen des nächsten Datensatzes mit HReadNext(...)

Meine Frage für den nächsten Versuch:
- Welche Möglichkeit habe ich die Anzahl der Datensätze eines Querys zu ermitteln, außer mit den genannten Funktionen HNbRec(...), HLast(...) und HRecNum(...)?
- Und ist es möglich ein Query in eine Schliefe durchzulaufen (FOR = 1 TO nAnzahlDatensaetze) ohne den nächsten Daten mit HReadNext(...) lesen zu müssen? D.h. direkt den Datensatz zu adressieren (~wie ein Array) mit Query?

Welche sonstigen Ideen habt Ihr um diesen Flaschenhals zu entschärfen?

Re: Queryergebnisse (schnell) durchlafen

Verfasst: 25. September 2013, 09:33
von Magic
Ich zeige Euch welche Varianten ich bereits ausprobiert habe;

Code: Alles auswählen

tStartTime = Now()
for ALL QRY_Abwesenheit
	show_Abwesenheit(QRY_Abwesenheit.PS_NR,...)
end
Trace( "Abwesenheitsdaten malen (FOR ALL original): " + TimeDifference( tStartTime, Now() ) / 100 )

Code: Alles auswählen

tStartTime = Now()
nAnzahlDatensaetze = HNbRec( QRY_Abwesenheit )
Trace( "Anzahl Datensätze (" + nAnzahlDatensaetze + ") ermittlen mit HNbRec(...): " + TimeDifference( tStartTime, Now() ) / 100 )
tStartTime = Now()
IF nAnzahlDatensaetze > 0 THEN
	HReadFirst(QRY_Abwesenheit)
	FOR i = 1 TO nAnzahlDatensaetze
		show_Abwesenheit(QRY_Abwesenheit.PS_NR,...)	
		HReadNext(QRY_Abwesenheit)	
	END
END
Trace( "Abwesenheitsdaten malen (FOR i Variante): " + TimeDifference( tStartTime, Now() ) / 100 )

Code: Alles auswählen

tStartTime = Now()
HReadFirst(QRY_Abwesenheit)
WHILE HOut(QRY_Abwesenheit) = False
	show_Abwesenheit(QRY_Abwesenheit.PS_NR,...)	
	HReadNext(QRY_Abwesenheit)	
END
Trace( "Abwesenheitsdaten malen (WHILE HOut(..) Variante): " + TimeDifference( tStartTime, Now() ) / 100 )

Code: Alles auswählen

tStartTime = Now()
HReadFirst(QRY_Abwesenheit)
LOOP
	IF HOut()=True THEN BREAK
	show_Abwesenheit(QRY_Abwesenheit.PS_NR,...)	
    HReadNext(QRY_Abwesenheit)		
END
Trace( "Abwesenheitsdaten malen (LOOP Variante): " + TimeDifference( tStartTime, Now() ) / 100 )

Code: Alles auswählen

tStartTime = Now()
FOR EACH QRY_Abwesenheit 
	IF HOut()=True THEN BREAK	
	show_Abwesenheit(QRY_Abwesenheit.PS_NR,...)
END
Trace( "Abwesenheitsdaten malen (FOR EACH Variante): " + TimeDifference( tStartTime, Now() ) / 100 )
Die Zeitmessung ist bei allem Varianten in etwa gleich. Und vor allem zu lang.
Es ist definitiv der erste Zugriff auf das Query, der so lange dauert. Egal ob es das HNbRec(...) oder HReadFirst(..), etc. ist.
Was kann man dagegen machen?

Re: Queryergebnisse (schnell) durchlafen

Verfasst: 25. September 2013, 14:01
von Magic
Weiter getestet ...

Habe jetzt versucht die Daten mit HExecuteSQLQuery(...) zu holen.
Das Ergebnis ist das gleiche. D.h. sobald ich das erste Mal auf die Daten zugreife (mit HReadFirst(...) oder HNbRec(MyQRY), etc. ) dauert es ...
Die Zugriffe danach sind dagegen sau schnell, fast nicht messbar.

Wie werden die Daten überhaupt verwaltet? Wo befinden sich die Ergebnisse wenn ich ein Query ausgeführt habe? Schon im Speicher auf den lokalen Rechner oder immer noch auf dem Server?

Liegt es vielleicht an der Datenquelle? Da das Ausführen des Querys recht flott geht, kann ich es mir aber nicht so richtig vorstellen. Die Daten liegen auf einem Microsoft SQL und auf einem ADS Server und der Connect geht über OLEDB.
Wenn es nicht so viele Tabellen wären und das Projekt nicht bereits ausgeliefert wäre, würde ich den Versuch wagen und die Daten auf den HyperFile SQL portieren. Aber nur um es zu probieren? Es sind ca. 20 Tabellen.
Außerdem, den Connenct auf ADS kann ich nicht umgehen.

Re: Queryergebnisse (schnell) durchlafen

Verfasst: 26. September 2013, 10:00
von Herbert
Bin gespannt, wer hierzu noch Informationen mitgeben kann.
Ich selber brauche kein OLEDB, daher kenne ich dies nicht.

Ein Ausweg könnte sein, dass du nach dem HExecuteQuery das Ergebnis mit HToFile speicherst und anschliessend mit HDeclareExternal ansprichst, was mit normalen Befehlen weiterlaufen kann (z.B. FileToMemoryTable)

Re: Queryergebnisse (schnell) durchlafen

Verfasst: 27. September 2013, 12:16
von Lewi
Hi,
wenn Du über hExeCuteQuerry() eien MS-SQL Datenbank abfragst, dann setzt das voraus, dass Du "nativen" Zugriff auf den SQL-Server hast und eine entsprechende Analysis. "Nativ" bedeutet in diesem Zusammenhang, dass WinDev über ein kostenpflichten Treiber den Zugriff auf MS-SQL ermöglicht.

Ich selbst arbeite mit WinDev im Zusammenhang mit Fremd-Datenbanken ausschließlich über ODBC (Informix, MS-SQL). Insofern beziehen sich meine Erfahrungen auf diese Konstellation.

Hier eine Beispiel-Funktion:

Code: Alles auswählen

Procedure Test(SQLServer, SQLStatement)
QuerryResult is datasource
nRecCount is int

Hourglas(true)
IF NOT HExecuteSQLQuery(QuerryResult,SQLServer, hQueryWithoutCorrection + hNoBind,SQlStatement) THEN
	Open(WIN_SQLError,HErrorInfo(hErrFullDetails))
	HourGlass(False)	
	HCloseConnection(SQLServer)
	RESULT False
END
WHEN EXCEPTION IN 
nRecCount=HNbRec(QuerryResult)  // Falls kein Ergebnis vom SQL-Server geliefert wird, was verschiedene Gründe haben kann, wird durch Ermittlung der Anzahl der DS ein Fehler erzeugt und es geht weiter mit dem "DO"
DO 		
	HourGlass(False)
	Info( "SQL-Statement lieferfte ein fehlerhaftes Ergbnis! ")
	HCloseConnection(SQLServer)	
	RESULT False
END

FOR EACH QuerryResult 
    // mache irgend etwas wie z.B. zeige die Nr an ...
   Trace(QuerryResult.Nr)
END
HCancelDeclaration(QuerryResult)
HCloseConnection(SQLServer)	
HourGlass(False)

Die Ausführung des Codes
"IF NOT HExecuteSQLQuery(QuerryResult,SQLServer, hQueryWithoutCorrection + hNoBind,sSQl)"
kann je nach Abfrage kurz oder lange dauern. Ich habe in der Praxis durchaus Abfragen, wo erst nach 10 Minuten das Ergebnis vom SQL-Server zurück geliefert wird. Das Ergebnis selbst befindet sich dann in der datasouce Variable "QuerryResult". Sobald das Ergebnis der Abfrage ermittelt wurde, sind die Daten im Speicher. Hier sollten im Zuge der Verarbeitung zu keinen zeitlichen Problem kommen.

Gruß, Olaf

Re: Queryergebnisse (schnell) durchlafen

Verfasst: 2. Oktober 2013, 10:17
von Magic
Hi,

... so viel, so viel neues, wo fange ich an?

1) Das Problem habe ich behoben.
2) Mein erster Instinkt war richtig, habe mich aber von "Zeitmessungen" blenden lassen.
3) Die von Olaf getätigte Aussage:
Lewi hat geschrieben:Sobald das Ergebnis der Abfrage ermittelt wurde, sind die Daten im Speicher.
verwirrt mich, den meine Test zeigen ein zur Aussage nicht passendes Ergebnis. Obwohl ich der Aussage, theoretisch auch zustimmen würde.
4)
Lewi hat geschrieben:wenn Du über hExeCuteQuerry() eine MS-SQL Datenbank abfragst, dann setzt das voraus, dass Du "nativen" Zugriff auf den SQL-Server hast und eine entsprechende Analysis.
Das ist in dem mir vorliegenden Projekt definitiv nicht der fall.


Jetzt mal ein wenig ausführlicher.
zu 4)
Wir haben keinen nativen Treiber für die MS-SQL und keinen für die ADS DB. Das Projekt hat eine Analysis. Auf alle Tabellen (SQL und ADS) wird über einen OLEDB Treiber zugegriffen. Trotzdem wird hier hExecuteQuery(...) verwendet und es funktioniert auch. An den Stellen im Code, wo keine Query sondern ein zuvor zusammengebaute SQL Abfrage ausgeführt werden soll, wird hExecuteSQLQuery(...) verwendet. Das deckt sich also nicht mit der Aussage, dass für die Verwendung von hExecuteQuery(...) ein nativer Treiber vorhanden sein muss.

Zu 3)
Der Aussage von Olaf würde ich auch so zustimmen. Wie soll es denn auch anders sein? Aber in meinem Fall scheint etwas anders zu laufen. Denn ich messe die Zeit für das Ausführen von hExecuteQuery(...) und stelle fest, dass die Abfrage, je nach Netzauslastung zwischen 1-2 Sek. braucht. Das ist in Ordnung. Danach greife ich auf das Ergebnis zu und messe wiederum die Zeit. Sind die Daten bereits im Speicher, sollte es hier zu keinen größeren Verzögerungen kommen. Aber bei mir dauert der Erste Zugriff auf das Ergebnis, gemessene 20 - 60 Sek. und zwar unabhängig von der Anzahl der Datensätze, also auch bei einem. Ist der erste Satz gelesen, läuft alles in Sekundenbruchteilen durch. Die Verzögerung ist für die Anwendung nicht akzeptabel.

Zu 2 und 1)
Als ich das Projekt bekommen habe um die Performance zu verbessern, habe ich spontan gesagt: "Liegt mit hoher Wahrscheinlichkeit an nicht optimierten Datenbankzugriffen". Das ist zumindest - aus meiner Erfahrung - in den meisten Fällen, die ich bis dato bearbeitet habe der Fall und zwar unabhängig von Programmiersprache und System.
Deshalb habe ich die Zeit gemessen und war mit den 1-2 Sek. für die Abfrage zufrieden. Leider stimmte damit meine vorher getätigte Aussage nicht und ich musste weiter suchen.
Die Engstelle habe ich aber recht rasch ausfindig gemacht. Wir in vorherigem Post geschrieben, stockte immer der erste Zugriff auf das Ergebnis des Querys. Was also machen? Nach vielen erfolglosen Versuchen, habe ich es einen Tag liegen gelassen und dann mit vielleicht neuen Ideen dran zu gehen.
Ich fing also wieder von Vorne an. Und da ich aus der eigenen Erfahrung her sagen kann, dass die Engstellen zu geschätzten 90 - 65% der Fälle, schlecht programmierte DB Abfragen sind, schaute ich mir den Query genauer an, obwohl die Zeitmessung an dieser Stelle in Ordnung war.
Im Query wurden Daten aus zwei Tabellen geholt. Verknüpft mit einem Join. Das Fatale, die eine Tabelle liegt auf dem MS-SQL, die andere auf dem ADS Server. Auf beide erfolgt der Zugriff per OLEDB. Ich habe testweise den Query umgebaut, so dass ich nicht in einer Abfrage auf zwei Tabellen, auf zwei unterschiedlichen Servern zugreifen muss und die Where Klausen optimiert.
Das Ergebnis ist jetzt so wie man es erwarten würde. Die Ausführung des Query dauert einen Augenblick, danach - also beim Zugriff auf die gelieferten Daten - gibt es keine relevante Verzögerung mehr. Somit ist das Problem erledigt, es bleiben aber Fragen offen. Vielleicht war in meinen Fall das Konstrukt so untypisch, dass es solche (unvorhersehbare?) Folgen hatte.
Zumindest stimmt somit meine erste Diagnose.


P.S. Statt mit Trace() die gemessene Zeit auszugeben, kann man natürlich auch die WinDev Funktionalität unter Project->Performance profiler nutzen. Den Punkt habe ich aber erst später entdeckt.

Re: Queryergebnisse (schnell) durchlafen

Verfasst: 3. Oktober 2013, 17:07
von Lewi
Laut Online-Hilfe erwartet hExeCuteQuerry eine Querry, die mittels des Querry-Editor erstellt wurde. Dieser setzt wiederum voraus, dass eine Tabellenbeschreibung in der Analysis vorhanden ist. In Deinem Fall wirds Du die externe MS-SQL Tabllen wie auch die ADS-Tabellen in der Analysis nachgebaut haben, um eine Querry zu erstellen, beim Ausführen der Querry Du aber ein Connect zu den externen Datenbanken angibst. So geht es natürlich auch. ;-)

Ich wüsste jetzt nicht warum nach dem HExecuteSql() Latenzen entstehen sollten. Das ein SQL-Statemant immer eine (gesamte) "Menge" zurück gibt, die der entsprechende SQl-Server liefert, erschließt sich mir das Problem von Latenzen nicht. Wenn beim Durchlauf der Ergebnis-Querry Latenzen auftreten, dann wohl m.E. aus dem Grund, dass Satzzeiger über Hseek, hRreadDFirst etc. positioniert werden.

Re: Queryergebnisse (schnell) durchlaufen

Verfasst: 9. Oktober 2013, 06:50
von Magic
Lewi hat geschrieben: In Deinem Fall wirds Du die externe MS-SQL Tabllen wie auch die ADS-Tabellen in der Analysis nachgebaut haben, um eine Querry zu erstellen
Ja, der Entwickler hat die benötigten Tabellen (SQL & ADS) in der Analysis nachgebaut.
Ich schließe jetzt daraus, dass ich, um auf Daten eines SQL und ADS Servers zugreifen zu können, die Analysis nicht unbedingt brauche. Will ich die Query nutzen, wird die Analysis vorausgesetzt.

Ist noch alles neu für mich. Arbeite gerade so langsam das Tutorial durch.