Delphi Performance: Lesen aller Werte unter einem Feld in einem Dataset

7

Wir versuchen, einige Performance-Korrekturen aus einer TADOQuery zu finden. Momentan durchlaufen wir die Datensätze mit 'ohne Q.eof do ...' ... Q.next-Methode. Für jeden lesen wir die ID und den Wert jedes Datensatzes und fügen sie jeder Combobox-Liste hinzu.

Gibt es eine Möglichkeit, alle Werte eines bestimmten Feldes auf einmal in eine Liste zu konvertieren? Anstatt den Datensatz durchzublättern? Es wäre sehr praktisch, wenn ich etwas wie ...

machen könnte %Vor%

Ich weiß, das ist kein richtiger Befehl, aber das ist das Konzept, nach dem ich suche. Auf der Suche nach einer schnellen Antwort und Lösung (wie immer, aber das ist ein wirklich dringliches Leistungsproblem).

    
Jerry Dodge 04.11.2011, 22:47
quelle

8 Antworten

3

Es gibt einige großartige Vorschläge anderer Leute, die Sie in Delphi implementieren sollten. Sie sollten sie berücksichtigen. Ich werde mich auf ADO konzentrieren.

Sie haben nicht angegeben, was der Back-End-Datenbankserver ist, daher kann ich nicht zu spezifisch sein, aber es gibt einige Dinge, die Sie über ADO wissen sollten.

ADO RecordSet

In ADO gibt es ein RecordSet-Objekt. Dieses RecordSet-Objekt ist in diesem Fall grundsätzlich Ihr ResultSet. Das Interessante an der Iteration durch das RecordSet ist, dass es immer noch mit dem Provider gekoppelt ist.

Cursortyp

Wenn Ihr Cursortyp Dynamisch oder Delphi's Standard-Keyset ist, dann wird der Provider jedes Mal, wenn das RecordSet eine neue Zeile vom Provider anfordert, prüfen, ob es irgendwelche Änderungen gab, bevor es den Datensatz zurückgibt.

Also, für die TADOQuery, wo Sie nur die Ergebnismenge lesen, um die Combobox aufzufüllen, und es wird sich wahrscheinlich nicht geändert haben, sollten Sie den statischen Cursortyp verwenden, um zu vermeiden, nach aktualisierten Datensätzen zu suchen.

Wenn Sie nicht wissen, was ein Cursor ist, wenn Sie eine Funktion wie Weiter aufrufen, bewegen Sie den Cursor, der den aktuellen Datensatz darstellt.

Nicht jeder Provider unterstützt alle Cursortypen.

CacheSize

Die Standard-Cache-Größe von Delphi und ADO für ein RecordSet ist 1. Das ist 1 Datensatz. Dies funktioniert in Kombination mit dem Cursortyp. Die Cachesize teilt dem RecordSet mit, wie viele Datensätze gleichzeitig abgerufen und gespeichert werden.

Wenn Sie einen Befehl wie Next (wirklich MoveNext in ADO) mit einer Cachegröße von 1 ausgeben, hat der RecordSet nur den aktuellen Datensatz im Speicher. Wenn er diesen nächsten Datensatz holt, muss er ihn erneut vom Provider anfordern. Wenn der Cursor nicht statisch ist, hat der Provider die Möglichkeit, die neuesten Daten abzurufen, bevor der nächste Datensatz zurückgegeben wird. Eine Größe von 1 ist also sinnvoll für Keyset oder Dynamic, weil der Provider die aktualisierten Daten erhalten soll.

Offensichtlich gibt es bei einem Wert von 1 bei jeder Bewegung des Cursors eine Kommunikation zwischen dem Provider und RecordSet. Nun, das ist ein Overhead, den wir nicht wollen, wenn der Cursortyp statisch ist. Wenn Sie also Ihre Cache-Größe erhöhen, verringert sich die Anzahl der Roundtrips zwischen dem RecordSet und dem Provider. Dies erhöht auch Ihre Speicheranforderungen, aber es sollte schneller sein.

Beachten Sie außerdem, dass bei einer Cachegröße größer als 1 für Keyset-Cursor, wenn sich der gewünschte Datensatz im Cache befindet, er sie nicht erneut vom Provider anfordert, was bedeutet, dass die Updates nicht angezeigt werden.

    
Marcus Adams 05.11.2011, 15:34
quelle
13

Betrachten wir Ihren Kommentar, hier ein paar Vorschläge:

Es gibt ein paar Dinge, die in dieser Situation wahrscheinlich ein Flaschenhals sind. Der erste sieht die Felder wiederholt nach oben. Wenn Sie FieldByName oder FindField innerhalb Ihrer Schleife aufrufen, verschwenden Sie CPU-Zeit, indem Sie einen Wert neu berechnen, der sich nicht ändert. Rufen Sie FieldByName einmal für jedes Feld auf, von dem Sie gerade lesen, und weisen Sie sie stattdessen lokalen Variablen zu.

Wenn Sie Werte aus den Feldern abrufen, rufen Sie AsString oder AsInteger oder andere Methoden auf, die den gesuchten Datentyp zurückgeben. Wenn Sie von der TField.Value -Eigenschaft lesen, verschwenden Sie Zeit für variant Conversions.

Wenn Sie einem Delphi-Kombinationsfeld eine Reihe von Elementen hinzufügen, handelt es sich wahrscheinlich um eine String-Liste in Form der Items -Eigenschaft. Legen Sie die Eigenschaft Capacity der Liste fest und vergewissern Sie sich, dass Sie BeginUpdate aufrufen, bevor Sie mit der Aktualisierung beginnen, und rufen Sie am Ende EndUpdate auf. Das kann einige interne Optimierungen ermöglichen, die das Laden großer Datenmengen schneller machen.

Je nach der von Ihnen verwendeten Kombinationsbox kann es Probleme beim Umgang mit einer großen Anzahl von Elementen in der internen Liste geben. Sehen Sie, ob es einen "virtuellen" Modus hat, in dem Sie nicht einfach alles im Voraus laden müssen, sondern ihm einfach mitteilen, wie viele Elemente benötigt werden. Wenn es abgesetzt wird, ruft es einen Ereignishandler für jedes Element auf, das angezeigt werden soll auf dem Bildschirm, und Sie geben es den richtigen Text zum Anzeigen. Dies kann bestimmte UI-Steuerelemente wirklich beschleunigen.

Auch sollten Sie sicherstellen, dass Ihre Datenbankabfrage selbst schnell ist, aber die SQL-Optimierung würde den Rahmen dieser Frage sprengen.

Und schließlich ist der Kommentar von Mikael Eriksson definitiv bemerkenswert!

    
Mason Wheeler 04.11.2011 23:19
quelle
8

Sie können Getrows verwenden. Sie geben die Spalte (n) an, an der Sie interessiert sind, und gibt ein Array mit Werten zurück. Die Zeit, die zum Hinzufügen von 22.000 Zeilen zu einem Kombinationsfeld benötigt wird, reicht von 7 Sekunden mit der while not ADOQuery1.Eof ... -Schleife bis zu 1.3 Sekunden in meinen Tests.

Beispielcode:

%Vor%

Wenn Sie mehr als eine Spalte im Array haben möchten, sollten Sie ein variantes Array als dritten Parameter verwenden.

%Vor%     
Mikael Eriksson 05.11.2011 10:28
quelle
3

Sie können Schleifen nicht vermeiden. "Sehr lange Zeit" ist relativ, aber wenn das Abrufen von 20000 Datensätzen zu lange dauert, stimmt etwas nicht.

  • Überprüfen Sie Ihre Anfrage; vielleicht kann das SQL verbessert werden (fehlender Index?)
  • Zeigen Sie den Code Ihrer Schleife, in dem Sie Elemente zur Combobox hinzufügen. Vielleicht kann es optimiert werden. (Aufruf von FieldByName wiederholt in einer Schleife? Verwenden von Varianten zum Abrufen von Feldwerten?)
  • Achten Sie darauf, ComboBox.Items.BeginUpdate; vor der Schleife und ComboBox.Items.EndUpdate nach.
  • aufzurufen
  • Verwenden Sie einen Profiler, um den Engpass zu finden.
Ondrej Kelle 04.11.2011 23:13
quelle
3

Sie könnten versuchen, alle Daten in ein ClientDataSet zu übertragen und dies zu wiederholen, aber ich denke, das Kopieren der Daten auf das CDS macht genau das, was Sie gerade tun - Schleifen und Zuweisen.

Was ich einmal getan habe, war, Werte auf dem Server zu verketten, sie in einer Menge an den Client zu übertragen und sie erneut zu teilen. Dadurch wurde das System tatsächlich schneller, da die erforderliche Kommunikation zwischen Client und Server reduziert wurde.

Sie müssen vorsichtig sein, wo der Leistungsengpass ist. Es könnte auch die Combobox sein, wenn Sie GUI-Aktualisierungen beim Hinzufügen von Werten nicht blockieren (besonders wenn wir über 20K-Werte sprechen - das ist eine Menge zu scrollen).

Bearbeiten: Wenn Sie die Kommunikation nicht ändern können, könnten Sie sie vielleicht asynchron machen. Fordern Sie neue Daten in einem Thread an, halten Sie die Benutzeroberfläche ansprechend, füllen Sie die Combobox, wenn die Daten vorhanden sind. Es bedeutet, dass der Benutzer eine leere Combobox für 5 Sekunden sieht, aber zumindest kann er in der Zwischenzeit etwas anderes tun. Ändert jedoch nicht die benötigte Zeit.

    
Heinrich Ulbricht 04.11.2011 23:03
quelle
3

Ist Ihre Abfrage auch an einige datensensitive Steuerelemente oder eine TDataSource gebunden? Wenn dies der Fall ist, führen Sie die Schleife in einem Block DisableControls und EnableControls aus, damit Ihre visuellen Steuerelemente nicht jedes Mal aktualisiert werden, wenn Sie in einen neuen Datensatz wechseln.

Ist die Liste der Elemente relativ statisch? Wenn dies der Fall ist, sollten Sie beim Start der Anwendung eine nicht-visuelle Instanz einer Combobox erstellen, möglicherweise innerhalb eines separaten Threads, und dann beim Erstellen des Formulars Ihre nicht-visuelle Combobox der visuellen Combobox zuweisen.

    
Sam M 05.11.2011 04:15
quelle
1

Verwenden Sie DisableControls und EnableControls, um die Leistung des linearen Prozesses im Dataset zu erhöhen.

%Vor%     
MajidTaheri 03.05.2012 18:25
quelle
0

Ich bin mir nicht sicher, ob das helfen wird, aber mein Vorschlag wäre, nicht direkt zu ComboBox hinzuzufügen. Laden Sie stattdessen zu einem lokalen TStringList , machen Sie das so schnell wie möglich und verwenden Sie dann TComboBox.Items.AddStrings , um alle auf einmal hinzuzufügen:

%Vor%     
Ken White 05.11.2011 01:12
quelle

Tags und Links