![]() |
Optimierung Datenbankzugriff Firebird
Moin allerseits,
ich greife mit den IBDac-Komponenten auf eine Firebird-Datenbank zu, in der bereits 16 Tabellen existieren. Beim Verbinden mit den Tabellen vergehen knapp 8 Sekunden, und mir kommt das doch ein wenig lang vor. Zum Projekt: Auf einem Datenmodul sitzen eine TIBCConnection, 16 TIBCTable mit den dazugehörenden TIBCDataSource und zwei TIBCTransaction. In OnShow der Hauptform rufe ich meine Verbindungs-Funktion auf:
Delphi-Quellcode:
Das dauert meinen Messungen nach in der IDE im Debug-Mode 141 Millisekunden, dagegen als Exe Release gestartet nur noch 93 ms. Der Verbindungsaufbau zur Datenbank ist also nicht das Problem, wie ich oben schon andeutete. Die 16 TIBCTable-Komponenten auf Active := True zu setzen, dauert:
function TDatMod.Verbinden_Datenbank : Boolean;
begin Trennen_Datenbank; ConMain.Username := Adr_Types.URec.DB_User; ConMain.Password := Adr_Types.URec.DB_Pass; ConMain.Options.Role := Adr_Types.URec.DB_Role; ConMain.Server := 'localhost'; ConMain.SQLDialect := 3; ConMain.LoginPrompt := False; ConMain.Options.Charset := 'UTF8'; ConMain.Options.UseUnicode := True; IF Adr_Types.DB_Embedded THEN BEGIN ConMain.ClientLibrary := Adr_Types.URec.Pfad_Main + Adr_Types.Client_EmbedDLL; ConMain.Database := Adr_Types.URec.Pfad_Main + Adr_Types.DatenbankDateiName; END ELSE BEGIN ConMain.ClientLibrary := Adr_Types.URec.Pfad_Main + Adr_Types.Client_ServerDLL; ConMain.Database := Adr_Types.URec.Pfad_DB + Adr_Types.DatenbankDateiName; END; TRY ConMain.Connect; EXCEPT on e:exception DO ShowMessage('Fehler beim Verbinden mit der Datenbank: ' + e.Message); END; Result := ConMain.Connected; end; IDE Debug-Mode 8827 ms, Exe Release 7155 ms. Danach kommt das Setzen diverser Einstellungen von Tabellen, Sortierreihenfolgen usw., das dauert: IDE Debug-Mode 2422 ms, Exe Release 1828 ms. Die Tabellen setze ich mit TIBCTable.Open aktiv:
Delphi-Quellcode:
Von einem freundlichen Programmiererkollegen mit Win 7 und Radstudio XE2 erhielt ich folgende Testergebnisse, nachdem er mein Projekt auf seinem System getestet hatte:
function TDatMod.Verbinden_Tabellen: Boolean;
begin Result := False; TRY Tab_Benutzer.Open; Tab_Rechte.Open; Tab_Geschlecht.Open; ... Result := True; EXCEPT on e:exception DO ShowMessage('Fehler beim Verbinden mit den Tabellen: ' + e.Message); END; end; *** Wie oben Win 7 32 Bit Debug IF NOT DatMod.Verbinden_Datenbank THEN 70 IF NOT DatMod.Verbinden_Tabellen THEN 5129 Set_Einstellungen; 2327 *** Wie oben Win 7 64 Bit Debug IF NOT DatMod.Verbinden_Datenbank THEN 58 IF NOT DatMod.Verbinden_Tabellen THEN 3867 Set_Einstellungen; 1847 *** Wie oben Win 7 32 Bit Release IF NOT DatMod.Verbinden_Datenbank THEN 60 IF NOT DatMod.Verbinden_Tabellen THEN 5076 Set_Einstellungen; 2306 *** Wie oben Win 7 64 Bit Release IF NOT DatMod.Verbinden_Datenbank THEN 64 IF NOT DatMod.Verbinden_Tabellen THEN 3855 Set_Einstellungen; 1777 *** Wie oben Win 7 32 Bit Debug IDE IF NOT DatMod.Verbinden_Datenbank THEN 160 IF NOT DatMod.Verbinden_Tabellen THEN 5957 Set_Einstellungen; 2532 DatMod.ZweiteVerbindung; 55 *** Wie oben Win 7 64 Bit Release IF NOT DatMod.Verbinden_Datenbank THEN 196 IF NOT DatMod.Verbinden_Tabellen THEN 3854 Set_Einstellungen; 1579 DatMod.ZweiteVerbindung; 48 Auf seinem Rechner, der ungefähr dieselbe Taktfrequenz und Ausstattung wie meiner hat (2x3 GHz, 4 GB Speicher) geht es also am schnellsten, wenn er eine 64-Bit-Anwendung erzeugt und mit einer 64-Bit-Firebird-Installation arbeitet. Leider kann ich nur 32-Bit-Anwendungen erzeugen, aber ich glaube, daß man das irgendwie optimieren kann, hab aber im Moment keinen blassen Schimmer, wo ich ansetzen könnte. Der Eintrag DatMod.ZweiteVerbindung; in den letzten beiden Versuchen beschreibt die Zeit, die eine zweite Verbindung mit nur einer Tabelle benötigt (incl. Datenbankverbindung und Tabellen-Komponente auf aktiv setzen. Bei dieser zweiten Verbindung wurden keine Komponenten aufs Datenmodul geschoben, sondern dieselben im Create des Datenmoduls erst erzeugt:
Delphi-Quellcode:
Die Verbindung wird bei dieser Zweitverbindung erst hergestellt, wenn die erste Verbindung bereits besteht:
procedure TDatMod.DataModuleCreate(Sender: TObject);
begin ConMainX := TIBCConnection.Create(DatMod); TransMainX := TIBCTransaction.Create(DatMod); Tab_LandX := TIBCTable.Create(DatMod); Dsrc_LandX := TIBCDataSource.Create(DatMod); end;
Delphi-Quellcode:
Wäre es angesichts der Tatsache, daß die Zweitverbindung nur 48 ms inkl. einer Tabelle benötigt, hilfreich, alle Komponenten erst zur Laufzeit zu erzeugen? Ich könnte das ja mühevoll austesten, aber vielleicht hat ja bereits jemand diesen Versuch gemacht.
procedure TDatMod.ZweiteVerbindung;
begin ConMainX.Username := Adr_Types.URec.DB_User; ConMainX.Password := Adr_Types.URec.DB_Pass; ConMainX.Options.Role := Adr_Types.URec.DB_Role; ConMainX.Server := 'localhost'; ConMainX.SQLDialect := 3; ConMainX.LoginPrompt := False; ConMainX.Options.Charset := 'UTF8'; ConMainX.Options.UseUnicode := True; ConMainX.ClientLibrary := Adr_Types.URec.Pfad_Main + Adr_Types.Client_ServerDLL; ConMainX.Database := Adr_Types.URec.Pfad_DB + Adr_Types.DatenbankDateiName; ConMainX.DefaultTransaction := TransMainX; ConMainX.Connect; TransMainX.Active := True; Tab_LandX.Connection := ConMainX; Tab_LandX.TableName := 'LAND'; Tab_LandX.KeyGenerator := 'GEN_LAND_ID'; Tab_LandX.KeyFields := 'IDX_LAND'; Tab_LandX.IndexFieldNames := ''; Tab_LandX.Options.BooleanDomainFields := True; Tab_LandX.Open; Dsrc_LandX.DataSet := Tab_LandX; Dsrc_LandX.Enabled := True; end; Was gäbe es sonst noch für Möglichkeiten, die Herstellung der Verbindungen zu beschleunigen? |
AW: Optimierung Datenbankzugriff Firebird
Moin...
die Lösung ist einfach... Zitat:
|
AW: Optimierung Datenbankzugriff Firebird
Ich vermute auch, dass es an den beim Öffnen zu ladenenden Datenmengen liegt. Ich bezweifle, dass alle 16 sofort benötigt werden und, wie haentschman schon schrieb, die bei den benötigten Tabellen nicht deren vollständigen Inhalt.
Zudem kannst du auch mit der embedded Dll auf einen Server zugreifen ( dann über IP) |
AW: Optimierung Datenbankzugriff Firebird
Moin,
das hängt von verschiedenen Faktoren ab, u.a. deinem Tabellendesign, aber auch generell davon, das du halt TTable Varianten benutzt. Da sind längst nicht alle Komponentenvarianten schnell und die treiben meistens einen Riesen Overhead gegenüber reinen SQL Komponenten, bei denen du bestimmt, welches SQL ausgeführt wird und wann es ausgeführt wird. Wenn du sehr viele Felder in deinen Tabellen hast müssen wesentlich mehr Metadaten geladen werden. Was Firebird wirklich macht kannnst du z.B. bei FB25 mit der TraceAPI erkennen ![]() Dazu kommt dann noch die von dir benutzte Firebird Version. Wenn es der Superclassic oder classic ist und du anden Cache Buffers nichts geändert hast, dann liegt wohl auch da der Hund begraben. Beim Superserver, den ich für jeden empfehle, der sich weniger mit FB auskennt, der default cache buffer mit 2048 ok, beim classic oder superclassic mit 75 viel zu gering. sehe kannst du den Wert in der Datenbankstatistik oder hier ![]() Ich hab mir die statischen Datenmodule mit hunderten von Komponenten schon vor Jahren abgewöhnt. Bei diversen Delphi Consulting Jobs sehe ich aber noch Datenmodule mit hunderten von Datasets, das ist kaum wartbar, entsteht aber meistens im Laufe der Jahre weil man mal so angefangen hat und nun an zig tausend TField Objekten und TDataset Events was rangebastelt hat, was man nicht mal eben ändern kann. Ich erzeuge alle Datasets zur Laufzeit, aber performancemäßig macht das eigentlich keine Unterschied. Wenn es geht, dann vermeide generell TTable Varianten, da geht Komfort für den Programmierer zu Lasten Performance für Endanwender. Evtl. hast du aus der TraceAPI ode rdurch geänderte Cache Buffers ja schon neue Erkenntnisse |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Schau Dir auch mal im Taskmanager den Ramverbrauch deiner EXE an, da wirst Du sehen je mehr Daten da sind desdo mehr ram verbraucht deine exe. PS: bei 2 GB ist dann schluß .. bei 32 bit anwendung .... |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
|
AW: Optimierung Datenbankzugriff Firebird
@alle: Das sind in er Tat sehr nützliche Tipps und Hinweise, die ihr da parat habt. Es wird echt Zeit, daß ich mich mit all diesen Optimierungsmöglichkeiten eingehender befasse. Immerhin hab ich heute nacht noch bis gegen 4 Uhr herumprobiert und getestet. Ich hab da eine Tabelle, die enthält 10 Felder und derzeit ca. 300.000 Datensätze. Bin noch am Einlesen, am Ende werden es knapp eine Million Datensätze sein, dazu 5 Untertabellen mit jeweils 2 bis 4 Feldern, die auch bis zu 50.000 Datensätze fassen.
Auf die meisten Tabellen werden beim Start Locate-Methoden angewandt, um die zuletzt bearbeiteten Datensätze für den Anwender sofort sichtbar zu machen. Auch das werde ich nochmal überdenken, denn eigentlich benötigt der Anwender nur zwei oder drei Tabellen, mit denen er wirklich ständig arbeitet. Das sollte auch etwas Geschindigkeit beim Start bringen. In den Options der Tab-Kompos habe ich bereits das Property QueryRecordCount deaktiviert. Das hat die Sache schon mal ein wenig beschleunigt, denn das Durchzählen der Datensätze nimmt natürlich auch Zeit in Anspruch. Ich werde den RecordCount bei den entsprechenden Tabellen in der Anwendung verwalten. @Holger Klemt & haentschman: Die Table-Komponente von IBdac soll ja laut Dokumentation ein direkter Abkömmling von TCustomIBCQuery sein. Aber ich probier's heute mal aus, problematische Table-Komponenten durch Queries zu ersetzen. Bei den Tabellen für Benutzereinstellungen, Benutzerrechte, Geschlecht und Anrede kann ich mir das schenken, die enthalten nur ein paar Datensätze. @Holger Klemt: Hab leider nur die Personal-Version von IBExpert. Aber die Database-Properties kann ich mir natürlich anzeigen lassen: Unter Buffer steht Pages auf 90 und KB auf 1440. Hab jetzt mal den Wert auf 10000 kb erhöht, dabei entstehen 625 Seiten und der Sweep-Interval steht auf 20000. Leider weiß ich nicht mehr genau, welche Firebird-Version (v2.5) ich installiert hatte, aber ich glaube mich zu erinnern, daß es er Classic-Server war. IBexpert schreibt mir das heraus, wenn ich die Servereigenschaften abfrage: Server Version: WI-V2.5.2.26539 Firebird 2.5 Server Implementation: Firebird/x86/Windows NT Service Version: 2 Zitat:
Zitat:
@alle: Nochmal vielen Dank an euch, ich werde jetzt gleich nach dem verspäteten Frühstück darangehen, eure Vorschläge umzusetzen. |
AW: Optimierung Datenbankzugriff Firebird
Meine Empfehlung wären ebenfalls Queries, die gezielt Teilmengen abrufen.
Mit Table Komponenten habe ich schon lange nicht mehr gearbeiet, gibt es da sowas wie max-records? Wenn Du bei Tables bleiben musst/willst, probier doch mal Filter auf PK Felder, die garantiert eine leere Menge ergibt (oder eine garantiert kleine~1 Datensatz z.B... Im weiteren Programmverlauf muss natürlich was sinnvolles in den Filter rein, Hauptsache, die große Tabellen werden nicht/niemals ungefiltert geöffnet und alles wird geladen. |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Die Obergrenze liegt so bei 16 bis 20 Stück. Es ist wichtig, die Abfragen thematisch auf mehrere Datenmodule zu verteilen und jedes Datenmodul möglichst so zu designen, dass es keine Abhängigkeiten zu anderen Datenmodulen hat. Du hast ganz richtig erkannt, dass zu viele Datasets auf einem Datenmodul extrem schwer zu warten sind. Deine Schlussfolgerung in Zukunft ALLE Datasets zur Laufzeit zu erzeugen ist aber auch nicht die Lösung. Du verschenkst damit Entwicklungszeit und erhöhst die Menge des Sourcecodes. (je mehr Sourcecode, umso mehr Fehlermöglichkeiten gibt es) |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Mit dem Umstellen von TIBCTable auf TIBCQuery habe ich das goldene Los gezogen, die Startzeit hat sich nun merklich verringert:
Code:
IF NOT DatMod.Verbinden_Datenbank THEN 124
IF NOT DatMod.Verbinden_Tabellen THEN 31 Set_Einstellungen; 2156 |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Erst auf der Ebene der Basisklasse habe ich dann die konkrete Implementation angesprochen, also ob das TQuery, TIBOQuery, TICQuery oder was auch immer war, solange es eine Dataset Ableitung war. Ich bekomme immer noch Tränen in den Augen, wenn ich sehe, wie viele Kundenprojekte zum Beispiel mal mit IBObjects realisiert wurden und wo man sich in ewige Abhängigkeit davon begeben hat. Wenn ich in einer Unit nur den Konstrutor auf ein andere Klasse portieren muß dauert das ggf. nur wenige Minuten, wenn ich aber in 200 Datenmodulen 5000 Datasetableitungen mit 50000 TField Definitionen anpassen muß, dann sieht das schon ganz anders aus, insbesondere wenn es auf einmal gewisse TFieldklassen, -properties oder events gar nicht mehr pder mit anderer Implementation gibt. Der Quelltextgenerator selbst hat danach dann auch noch (sofern nicht schon vorhanden) von dieser Klasse noch mal eine Ableitung erstellt, in der ich dann Methoden überschreiben konnte und völlig andere Implemetationen für komplexere Aufgaben umsetzen konnte. Der Quelltextgenerator hat neben den Objektklassen auch noch automatisch diverse include files erzeugt, die ich dann im eigentlichen Projekt eingebaut habe. Da der meiste Sourcecode aus dem Quelltextgenerator erzeugt wurde war dieser per Definition fehlerfrei. Ob der dann das gemacht hat, was er machen sollte, lag am Sourcecode vom Quelltextgenerator, aber in einem großen Projekt mit ca. 800 Tabellen musste für die Kompatibilitätsschicht zwischen Firebird und IBM AS/400 iSeries (oder wie auch immer die Kiste jetzt heißt) nach einem Update auf der AS/400 Seite und der Umstellung von Delphi/400 auf ODBC ziemlich viel geändert werden. Das wurde aber nur ein einer klasse exemplarisch manuell gemacht, bis es lief und dann der Quelltextgenerator angepasst, damit der die neuen Strukturen erzeugt. Die restlichen 799 Klasse dauerten danach weniger als eine Minute. Der Quelltextgenerator war übrigens eine ganz gruselige Implementation (TForm mit TMemo, TButton, einer TxDatabase, einer TxQuery) und einem endlosen OnClick Event, mit dem die gefundenen Metadaten aus der DB in Delphi kompatiblen Quellcode umgewandelt wurde. Einige werden sich evtl noch dran erinnern ... Das klassische RAD Prinzip von Delphi ist zwar am Anfang sehr produktiv, aber bei Projekten, die mehrere Jahre auch in größeren Teams entwickelt werden sollen, kann das schon mal gewaltig nach hinten losgehen. Ob man sich mal vor Jahren zum Einsatz von IBObjects entschlossen hat, Orpheus oder Turbopower Krams eingebaut hat oder was auch immer vor einigen Jahren mal angesagt war: Die konkrete Implementation an zigtausend Stellen im eigenen Quellcode lässt sich meistens mit Mühe und Suchen/Ersetzen noch lösen, der ganze Kram in den DFMs ist dann aber oft schon mit dem ersten Öffnen der Formulare mit fehlender oder falschen Komponentenversion hoffnungslos vergurkt. Ich habe im Laufe der Jahre diverse Softwarehäuser beraten, die oft auch meine Architektur übernommen haben und von denen habe ich viele Rückmeldungen, das dadurch die Fehler der Vergangenheit endlich hinter sich lassen konnten. Mittlerweile läuft das bei mir ohne eigene Units pro Tabelle über eine im Laufe der Jahre entwickelte Basisklasse, bei der alle Information zu jeder Tabelle bei ersten Zugriff im Client gecacht werden. Für lesen und schreiben der Daten werde eigene SQLs automatisch erzeugt oder, sofern vorhanden, nach bestimmten Namenskonvention vorhandene SPs benutzt. In meinem Modell ist die Datenbankstruktur Basis für alles andere, ich weiß aber auch, das viele das gerne andersherum lösen. Mein Vorteil ist, das meine Implementation mit völlig unterschiedlichen Datenbanken auch völlig unterschiedliche Anforderungen umsetzen kann, ich also für jeden Kunden individuell customizen kann. Ich glaub bei der Entwicklungszeit kann ich mit anderen Modellen sehr gut mithalten, die grundlegende Basis ist war nicht in 5 Tagen entwickelt, wenn man die dann aber stehen hat, geht es extrem fix. Mehr Komponenten bedeutet nicht zwangsläufig weniger Quellcode, umgekehrt aber auch nicht. Der ganze Bereich der Programmierung wäre aber auch extrem langweilig, wenn es immer nur die "eine Lösung" geben würde. |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Zitat:
Wenn du noch mehr Infos zu Firebird brauchst und dir mal hier und da Zeit dafür nehmen kannst, dann würde ich mal so nach und nach die Videos auf ![]() ![]() |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
![]() |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
|
Nachtrag zu: Optimierung Datenbankzugriff Firebird
Den Übeltäter für die langen Wartezeiten beim Starten meiner Anwendung habe ich jetzt eindeutig ausgemacht: Er heißt Sortieren. Nachdem ich alle Zuweisungen an IndexFieldNames – jenes Property der IBDac-Query-Komponenten, das für die Sortierung zuständig ist – entfernt hatte, warte ich ca 2 Sekunden darauf, daß die Anwendung zur Verfügung steht (ohne Ladezeit, da sind's dann schätzungsweise 3,5 Sekunden, also nur für den Aufbau der Anwendung im Speicher). Sobald ich jedoch sortieren lasse, ist die Geschwindigkeit dahin, ganz besonders bei meiner größten Tabelle mit derzeit um die 300.000 Einträgen und etlichen Abhängigkeiten. Eigentlich wird ja nicht die eigentliche Tabelle sortiert, sondern lediglich das View, das ich von dieser Tabelle in der DB angelegt habe. Die Sortierung des Views geht weitaus schneller als die der eigentlichen Tabelle mit ihren Lookup-Feldern. Leider war es mir bislang nicht möglich, die Sortierung des Vies zu beschleunigen, sie benötigt noch immer ca. 26 sec. Und da noch ca. 700.000 Einträge folgen werden, muß ich leider auf die Sortierung dieses speziellen Views beim Programmstart verzichten. Immerhin geht die Sortierung mittels Zuweisungen an IndexFieldNames mehr als 10 mal schneller als mittels Manipulation der Order-Klausel im Select-Befehl, die dauerte nämlich 157 Sekunden.
|
Nachtrag 2 zu: Optimierung Datenbankzugriff Firebird
Wenn ich die Sortierung ganz weglasse, benötigt der Start noch immer ca. 11 Sekunden. Kommentiere ich auch noch das Lokalisieren der zuletzt angezeigten Datensätze aus (locate), bin ich bei 46 Millisekunden Startzeit. Ich werde mich also darauf beschränken, die Haupttabelle, die noch gar nicht implementiert ist, zu sortieren und lokalisieren. Das werden bei einem Durchschnittsanwender sicher nicht mehr als 10- bis 20tausend Einträge werden ...
|
AW: Optimierung Datenbankzugriff Firebird
Und wenn du eine Query nimmst und im SQL-Statement sortierst?
|
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Immerhin geht die Sortierung mittels Zuweisungen an IndexFieldNames mehr als 10 mal schneller als mittels Manipulation der Order-Klausel im Select-Befehl, die dauerte nämlich 157 Sekunden. Ich hab ja jetzt fast nur noch Queries statt der Table-Komponenten, die bei IBdac im Grunde Datasets sind, wenn ich die Dokumentation richtig verstanden habe. Nur für die paar Tabellen, die sowieso nicht vom Anwender erweitert werden, wie Geschlecht, Anrede usw., hab ich die Tables gelassen. Die Sortiermöglichkeit in der Anwendung bleibt natürlich erhalten, nur überlasse ich es dem Anwwender, ob er die Sortierungen abspeichern will, eventuell sogar mit einer Warnung, daß das die Ladezeit des Programms enorm verzögert. Ich weiß nicht, wie andere das machen, und ich hab auch gerade kein Beispiel zur Hand, bei dem das Sortieren und Lokalisieren die Ladezeit der Anwendung nicht dermaßen vergrößert. Vielleicht ist das ja alles ganz normal :? |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Naja, ich werd' mich dennoch irgendwie durchwurschteln ... :wiejetzt: |
AW: Optimierung Datenbankzugriff Firebird
Moin,
die deutschen Videos sind im IBExpertLive, auf Yourtube ist wirklich nur eins auf deutsch |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
in XE4 unit Data.SqlExpr, das sieht mir nicht besonders clever aus. Der rennt sogar zwei mal durch die Datenmenge, wenn beim ersten Durchlauf keine Übereinstimmmung gefunden wurde. Und diese Implementation ist leider nicht unüblich, auch wenn andere Komponenten das ggf irgendwie anders lösen, da sollte man sich aber nicht drauf verlassen.
Code:
"Das war zu Zeiten der BDE" ist da also doch nicht ganz zutreffend ...
First;
while not EOF do begin if CheckValues(AFields, Values, CaseInsensitive, PartialLength) then break; Next; end; { if not found, reset cursor to starting position } bFound := not EOF; if not bFound then begin First; while not EOF do begin if CheckValues(SaveFields, StartValues, False, False) then break; Next; end; end; Result := bFound; |
AW: Optimierung Datenbankzugriff Firebird
Ich befürchte, Du wirst nicht darum herum kommen, dich zu entscheiden: Entweder hohe Ladezeiten oder einen etwas anderen Aufbau deines UX: Wieso soll der Anwender durch 300.000 Datensätze scrollen dürfen? Wieso muss er nach der (z.B.) Schuhgröße oder anderen, vollkommen unwichtigen, Eigenschaften sortieren können? Kann man sein Anliegen nicht anders lösen? Wir haben z.B. einmal ein ähnliches Problem dadurch gelöst, in dem der Anwender genau befragt wurde, wieso er denn darauf besteht, 100.000 Datensätze im Grid zu sehen: Dabei hat sich dann herausgestellt, das er diese Daten filtert und exportiert, um sie in EXCEL weiter zu verarbeiten. Und dazu hat er die Daten sortiert und bestimmte Bereiche rausgeschnippelt.
Also haben wir ihm kurzerhand ein Export-Tool geschrieben, was nun viel schneller geht und sind erstens unser Problem los (wie sortiert man eine Tabelle mit 50 Spalten und > 1Mio Zeilen performant in der GUI?) und zweitens sein Problem: Die Anwendung lief nur noch auf den aktuellsten PC mit schneller CPU und viel RAM. Unser Exporttool läuft überall. Die UX haben wir dahingehend verändert, das er zur Darstellung seiner Daten immer einen Filter setzen muss, d.h. es werden nicht alle Daten angezeigt. Lässt er diesen leer, dann weiß er wenigstens, warum das jetzt so lange dauert. Ich kenne FB nicht so gut, aber i.a. sind Sortieroperationen auf indexierten Spalten wesentlich schneller als auf Spalten ohne Index. Ist auch irgendwie logisch. Ich glaube auch, das FB den Index nicht verwendet, wenn dieser z.B. aufsteigend sortiert ist, man selbst aber absteigend sortieren möchte. Du könntest allen Spalten deiner Tabelle nun einen Index spendieren, aber das wäre irgendwie bescheuert, denn nun hast Du bei der Datenänderung ein Performanceproblem, wenn es viele sind. Zudem kann man den Index nicht mehr so gut verwenden, wenn man nach mehreren Spalten sortieren will. [QUOTE=IBExpert;1214261] Zitat:
Allerdings ist hier ein wirklicher Lapsus: Beim zweiten Durchlauf sollte man aus der Schleife heraus, wenn man den ursprünglichen Record ein zweites Mal untersucht. Da man ab hier eh alles schon geprüft hat, kann man gleich ans Ende springen und die Schleife beenden. Also: Locate funktioniert, wenn es etwas zu finden gibt und ist dann lahm (ok, und nicht wirklich clever), wenn es nichts zu finden gibt. |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Delphi-Quellcode:
startet (Position an den Anfang des DataSets setzen).
First
Da ist also nix von ab hier. Und der zweite Suchlauf sucht einfach nur nach dem Datensatz, der aktiv war, bevor die Suche begann. Wenn dieser der letzte Datensatz im DataSet ist, dann wird die Datenmenge im WorstCase zweimal komplett durchsucht. |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Dann aber nicht beschweren, das das Schreiben extrem lahm wird. Bei 16k Pagesize gehen bis zu 818 single column indices pro Tabelle. Dann werden bei jedem Insert oder Update nicht nur die zugehörige Datenpage neu geschrieben, sondern auch noch mal bis zu 818 Indexpages. Denk noch mal über die Anregungen von Furtbichler nach. Es ist ganz selten sinnvoll, in einem Grid hundertausende Records ungefiltert anzuzeigen. Aber bitte dann nicht mit den Filtereigenschaften arbeiten, die sind häufig genau so blöde implementiert wie locate. Ohne kleine Datenmenge fliegt dir auch ab einer gewissen Datensatzanzahl die Funktionalität mit win32 schnell um die Ohren, dann startet dein Programm ab einer gewissen Datensatzanzahl gar nicht mehr, weil das Grid inkl Overhead bei so vielen Records auch schnell mal die 2GB Grenze sprengt. Zitat:
Aber wie du schon sagst, 2 komplette Durchläufe gibt es nur wenn es keine Übereinstimmung gibt und vorher der letzte aktiv war. Dafür muss ich aber nicht alle Daten zum Client schicken, dafür gibt es eigenständige SQL Befehle. Bei kleinen Datenmenge ist die Implementation in den Komponenten fast egal, aber 300000 Records sind schon mal per se keine kleine Datenmenge |
AW: Optimierung Datenbankzugriff Firebird
Ich kann Furtbichlers Maßnahmen nur unterstreichen.
Wir bauen Systeme, die so arbeiten: Verwendung z.B. ADO mit MAXRECORDS z.B. gleich 1000 Ableitung von TADOQuery/TADODataset wandelt Filter & Sort in Where-Bedingung/Order By um. Mehrere Millionen Datensätze Im Backend wird generell eine Dimension rausgefiltert, bleiben z.B. 200000 DS in großen Datenmengen Öffnen/Sortieren von komplexen Datenmengen dauert leider noch mehrere Sekunden Das ist natürlich nicht gewünscht, dafür gibt es spezialisierte Suchmasken mit einer handvoll Suchfeldern (indiziert) Öffnen der großen Datenmengen über PK Filter aus Suchmaske. Suchmasken und Ergebnisdarstellung liegen deutlich unter 0,5 Sekunden Der Kunde kann unabhängig davon auch die großen Datenmengen auf jedem Feld filtern oder sortieren. Wenn er schlau ist, filtert er erst nährungsweise im Sekundenbereich auf eine kleine, relevante(!) Menge, dann sortiert er im Millisekundenbereich. Auch ein Locate auf vorgefilteterten, kleinen Datenmengen läuft hinreichend schnell. |
Gesammelte Antworten
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Achso, Frage: Was ist ein UX? User-Schnittstelle? |
AW: Gesammelte Antworten
Zitat:
Wirkt ein Index nicht, dann weil der Optimizer es nicht schnallt - oder der Entwickler-. Ein View ist nichts anderes als ein vorbereitetes Selectstatement. Du kannst es ja ausprobieren und die Zugriffszeiten testen. Die sollten gleich sein. (Ein View hat natürlich auch noch andere Zwecke, Interfaceschicht, Abstraktionsschicht, Berechtigungsschicht, ..), aber darum geht es hier ja gerade nicht. |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Zitat:
Zitat:
Column unknown BRASILIEN Die Procedure, die aufgerufen wird:
Delphi-Quellcode:
Wobei das Property FilterSQL laut Dokumentation nichts anderes macht als:
procedure TDatMod.SetFilter_OrteLand(Land: String);
begin View_Orte.FilterSQL := 'V_LAND=' + Land; end; // Example aus IbDac.pdf: // Query1.FilterSQL := 'Dept >= 20 and DName LIKE ''M%'''; Used to change the WHERE clause of SELECT statement and reopen a query. Übrigens derselbe Fehler, der auch beim Setzen der gewöhnlichen Filtereigenschaft auftritt, wie ich es zuerst versucht hatte:
Delphi-Quellcode:
Schon irgendwie seltsam ... vielleicht 'n Bug ...
procedure TDatMod.SetFilter_OrteLand(Land: String);
begin View_Orte.Filtered := False; View_Orte.Filter := 'V_LAND=' + Land; View_Orte.Filtered := True; end; |
AW: Optimierung Datenbankzugriff Firebird
Delphi-Quellcode:
View_Orte.FilterSQL := 'V_LAND=' + QuotedStr(Land);
|
AW: Optimierung Datenbankzugriff Firebird
nein, das ist korrekt:
Zitat:
Grüße |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
manchmal sieht man den Baum vor lauter Wald nicht mehr. Das funktioniert natürlich :thumb: |
AW: Optimierung Datenbankzugriff Firebird
Zitat:
Code:
Aber mich wundert etwas, dass TClientDataSet noch nicht vorgeschlagen wurde. Es geht doch darum, dass schon sehr viele Daten angezeigt und/oder sortiert werden sollen ? Was spricht da gegen ein ClientDataSet (CDS) ? Ohne jetzt eine Indexorgie auf Datenbankebene anzetteln zu müssen könnte man die Daten doch zunächst mal in ein CDS einlesen und dieses könnte man ja anzeigen (geht sogar mit DBgrid) oder es lässt sich einfach sortieren. Für Letzteres reicht ja schon lediglich die Angabe des zu sortierenden Feldes. Also z.B. so:
SELECT FIRST 1000 FROM ...
Code:
Was spricht dagegen, das so zu machen ?
CdsDM.CDS.IndexFieldNames := 'NR';
|
AW: Optimierung Datenbankzugriff Firebird
[QUOTE=Hansa;1214317]
Zitat:
Eine TComboBox, die die sortierten Ländernamen enthält und an Position 0 den Eintrag _Alle. Stellt der Anwender die ComboBox auf _Alle, entzieht ihm die Anwendung die Möglichkeit, beim Start sortieren zu lassen. Dafür liegt auf dem Panel noch eine TDBCheckBox, die mit bei Auswahl von 0 unchecked wird und gleichzeitig readonly. In der Doku bzw. Hilfe wird das dem Anwender dann genauer erklärt werden. Ich bin jetzt gerade dabei, weitere Auswahl-Combos zur Einschränkung der Datenmenge zu implementieren. Diese Lösung gefällt mir recht gut, weil sie mir sehr flexibel scheint und die Sortierzeit auf ein bis zwei Sekunden beschränkt, wobei ich bei weiteren Einschränkungen sicher noch unter eine Sekunde komme. Ich glaube nicht, daß die Sortierzeit bei Verwendung eines CDS wesentlich kürzer wäre, denn die Arbeit, die der Prozessor dabei zu verrichten hat, ist im Grunde dieselbe. |
AW: Gesammelte Antworten
Zitat:
|
AW: Gesammelte Antworten
Zitat:
|
AW: Optimierung Datenbankzugriff Firebird
aha, so soll das ja auch sein :-)
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:10 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz