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).
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.
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!
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%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.
FieldByName
wiederholt in einer Schleife? Verwenden von Varianten zum Abrufen von Feldwerten?) ComboBox.Items.BeginUpdate;
vor der Schleife und ComboBox.Items.EndUpdate
nach. 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.
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.
Verwenden Sie DisableControls und EnableControls, um die Leistung des linearen Prozesses im Dataset zu erhöhen.
%Vor% 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:
Tags und Links delphi performance list tadoquery