![]() |
Datenbank: Firebird • Version: 2.8 • Zugriff über: FireDAC
Denkanstoß MemTable/Cachetable FireDac
Hallo zusammen,
ich brauche mal einen Denkanstoß. Ich habe eine Tabelle in der ich variable Zeiträume speicher (Datum von/Bis) und auf die ich mehrmals im System darauf Zugreife um die jeweilige ID des Zeitraumes zu ermitteln. Hier bei bin ich auf die Idee gekommen, bei Systemstart eine FDQuery abzusetzen und diesen im Speicher offen zu lassen, bis ich entweder Refreshe oder das System beende. Ich Frage mich, ob mehrere Verbindungen zur Datenbank offen zu halten, eine sinnvolle Option ist oder ob dafür eine andere Möglichkeit gibt. Wie gestaltet es sich, wenn ein anderer User neue Einträge hinzufügt, wie bekommt der Client diese Aktualisierung mit? Vielen Dank Eppos |
AW: Denkanstoß MemTable/Cachetable FireDac
Brauchst ja nur eine Verbindung (Connection).
Schade ist, dass bei vielen DB-Komponenten die Daten entladen werden, wenn die Verbindung getrennt wurde. (z.B. Netzwerkproblemchen) Gut, im AfterConnect/Reconnect der Connection könnte man die Cache-Queries alle wieder öffnen. Man kann aber auch eine Abfrage machen und dann die Daten umkopieren, in eine MemTable/ClientDataSet, aber wenn man schon kopiert, dann wohl eher direkt in ein TDictionary<> oder eine TObjectList<TMyData> bzw. TList<TMyDataRecord>. Tja, für Updates brauchst du von der DB irgendwelche Notifications (manchmal auch Alert/Alerter genannt). Man könnte auch parallel Notifications via TCP/IP oder Sonstwas verteilen, die jeder Client verschickt, wenn er Änderungen macht, aber wenn die DB es über einen Trigger macht, isses einfacher und vorallem sicherer. ![]() ![]() ![]() |
AW: Denkanstoß MemTable/Cachetable FireDac
Zitat:
Also in Verbindung mit einem Memtable. Gruß BP |
AW: Denkanstoß MemTable/Cachetable FireDac
Zitat:
Die Frage die sich für mich stellt ist: Du willst eine ID haben. Ist die immer gleich oder ändert die sich mit jedem Neuerstellen der Abfrage? Ist die Ergebnismenge der Abfrage immer gleich, dann leg dir 'ne entsprechende Tabelle in der Datenbank an und greife darauf zu, anstatt bei jedem Programmstart immerwieder die gleiche Ergebnismenge zur Verarbeitung zu erstellen und für die Programmlaufzeit vorzuhalten. Und 'ne Query kann man beim Porgrammstart öffnen und bis zum Programmende geöffnet halten. Wenn die Menge der Daten nicht exorbitant hoch ist, also in den Arbeitsspeicher passt (ohne andere Programm negativ zu beeinflussen) dann hast Du damit letztlich eh schon 'ne Memorytable, mit entsprechend kurzen Zugriffzeiten. Zumindest klappt das bei mir mit meinem ollen Delphi 7 und Firebird 3 hervorragend. Gezielte Abfragen per SQL auf konkrete Daten (was bei dem Suchen einer ID für ein Zeitinterval ja wohl gegeben sein dürfte) gehen so schnell, dass man das eigentlich nicht merkt. |
AW: Denkanstoß MemTable/Cachetable FireDac
Wie gesagt, einfach die Daten (Records und Fields) in ein DataSet ohne DB-Verbindung (MemTable) kopieren.
Delphi-Quellcode:
Q := Datamodule.OpenSql('SELECT a, b FROM t');
try MemData.Close; // um den Inhalt schnell zu löschen, oder eben via "while MemData.RecordCount > 0 do MemData.Delete;" jeden Datensatz entfernen MemData.Open; // in MemData sind natürlich die gleichen Felder drin, wie im Query // eventuell auch noch MemData.DisableControls und MemData.EnableControls verwenden while not Q.EoF do begin MemData.Insert; //TDatamodule.CopyAllFields(Q, MemData); // Wir haben uns ein paar Hilfsfunktionen gebaut, aber man kann auch direkt mir den Query-Komponenten arbeiten. for i := 0 to Q.FieldCount - 1 do MemData.Fields[i].Value := Q.Fields[i].Value; { oder, weil ich unten auch nur zwei Felder nutze MemData.FieldByName['a'].Value := Q.FieldByName['a'].Value; MemData.FieldByName['b'].Value := Q.FieldByName['b'].Value; } MemData.Post; Q.Next; end; finally Q.Free; end;
Delphi-Quellcode:
Also Daten abrufen (SELECT), die Dataset-Records durchlaufen und die Daten in der Liste speichern.
//CacheList := TDirectory<string,string>.Create;
with Datamodule.OpenSql('SELECT a, b FROM t') do try CacheList.Clear; while not EoF do begin CacheList[AsString('a')] := AsString('b'); // bzw. FieldByName('a').AsString Next; end; finally Free; end; Und dann nur noch die Liste befragen
Delphi-Quellcode:
und wenn nötig den oberen Code nochmal ausführen, um die Liste zu aktualisieren.
S := CacheList['irgendwas']; // Peng, wenn es 'irgendwas' nicht gibt. (könnte man aber auch auf "Leerstring wenn nicht vorhanden" umbauen)
if CacheList.Contains('irgendwas') then S := CacheList['irgendwas']; if CacheList.TryGet('irgendwas', X) then S := X; ... |
AW: Denkanstoß MemTable/Cachetable FireDac
@Delphi.Narium
Die Frage die sich für mich stellt ist: Du willst eine ID haben. Ist die immer gleich oder ändert die sich mit jedem Neuerstellen der Abfrage? Die ist nicht immer gleich, konkret geht es um Geschäftsjahre: Beispiel 01.01.2020 - 31.12.2020. Jetzt wird anhand des Datums geschaut in welches Geschäftsjahr dies fliest und ob das Geschäftsjahr/der Monat freigegeben ist oder nicht. Ich hatte mir folgenden Plan ausgedacht: Abfrage bei Systemstart auf die Tabelle. Wenn ich die Abfrage benutze, zuerst ein Reopen (könnte ja sein, dass sich was geändert hat). Dann per Locate drauf zugreifen. Was aber vermutlich nicht geht, Locate mit ">= Datum_Von", "<= Datum_Bis"? Hat jemand eine Idee? |
AW: Denkanstoß MemTable/Cachetable FireDac
Die ID für den Zeitraum 01.01.2020 - 31.12.2020 ist doch wohl schon immer gleich?
Wie oft musst Du denn auf diese Daten zugreifen: andauernd, permanent (also quasi sekündlich oder öfter) oder eher sporadisch (also eher: Immer dann, wenn ein User einen Geschäftvorfall bearbeitet, was halt eher mal ein paar Minuten länger dauert)? Im zweiten Fall einfach bei Bedarf das Select absetzen und nachschauen. Dafür lohnt es sich nicht, die Daten permanent vorzuhalten und sie sind dann garantiert immer aktuell. Wenn Du Zitat:
Statt Locate gibt es auch noch Filter. Datenmenge? Nicht besonders groß (ein paar hundert Sätze dürfen es schon sein)?
Delphi-Quellcode:
Wenn Du für jeden Zeitraum genau einen Datensatz hast, stehst Du sofort dadrauf und brauchst nicht weitersuchen. Sind für einen Zeitraum mehrere Datensätze möglich, so musst Du die "Restmenge" durchgehen oder halt den Filter entsprechend erweitern, um als Ergebnis nur einen Datensatz zu erhalten. Ist die Filtermenge leer, dann gibt es das Gesuchte halt nicht.
Query.Filtered := false;
Query.Filter := Format('DatumVon = %s and DatumBis = %s',[QuotedStr(Var_DatumVon),QuotedStr(Var_DatumBis)]); Query.Filtered := true; // oder Query.Filtered := false; Query.Filter := Format('DatumVon = %d and DatumBis = %d',[2020,2021]); Query.Filtered := true; Im Filter kann man (fast) alles nutzen, was man im Where des SQLs auch "anstellen" kann, solange es um Bedingungen wie
Delphi-Quellcode:
geht. (Weitergehendes, wenn nötig, probieren.)
= >= <= <> and or
Beim Filter selbst muss Du mal ausprobieren, welcher Datentyp von Seiten Delphis und der DB am besten für Deinen konkreten Fall geeignet ist. Prinzipiell sollte es aber so gehen. Filter und Filtered gibt es ab TDBDataset. Alles, was davon abgeleitet ist, sollte diese Funktionalität aufweisen. |
AW: Denkanstoß MemTable/Cachetable FireDac
Vielen Dank für die ausführliche Beschreibung. Das mit dem Filter werde ich ausprobieren.
Der Zugriff erfolgt während einer Sekunde ca. 10 mal. Aber nur ein paar mal am Tag. Dies für eine Dauer von 5 Minuten. (Prüfprogramm) Von der memoryTable bin ich weggegangen und hin zu einem DataSet welchen ich vorhalte. Die ID für einen Zeitraum ist gleich jedoch kann sich innerhalb dieses Zeitraum Tages/Monatsbeschränkungen ergeben die es nicht erlauben den Datensatz zu verarbeiten. |
AW: Denkanstoß MemTable/Cachetable FireDac
Zitat:
Beispiel: In der Datenbank sind 10000 Datensätze. Du willst nur die 10 Datensätze haben vom/bis. Die kriegst du dann auch angezeigt. Trotzden wurden alle 10000 geladen. :roll: Besser eine Query mit den Kriterien...und es kommen nur 10 Datensätze. :thumb: |
AW: Denkanstoß MemTable/Cachetable FireDac
Wenn ein Prüfprogramm, dann muss kein User "endloslange" warten und kann nicht meckern.
Wäre es denn schlimm, wenn aus den 10 mal pro Sekunde ein 9,5 mal pro Sekunde wird und aus den ca. 5 Minuten ein ca. 5 Minuten und 3 Sekunden? Probier' doch einfach erstmal aus, ob es nicht sinnvoller ist, die Daten konkret bei Bedarf per SQL abzufragen. Wenn bei FireBird eine Abfrage (mit sehr ähnlicher Wherebedingung) sehr oft hintereinander genutzt wird, ist das "Zeugs" eh noch im Cache, im Speicher und (nach meiner Erfahrung) affig schnell. Es wäre also erstmal zu prüfen, ob das, was Du vorhast, aus Performanzgründen überhaupt erforderlich ist. Wenn Du Pech hast, dauert die MemoryTable und der Einleseaufwand, oder das Setzen und Nutzen der Filter insgesamt länger, als die (vermuteten) 3000 Abfragen (10 * pro Sekunde * 60 Sekunden pro Minute * 5 Minuten). @haentschman Du weißt, dass er vorhatte alle Daten in den Arbeitsspeicher (eine MemoryTable zu laden). Und jetzt sag mir nicht, dass die dann nicht im Arbeitsspeicher liegen. Wir tauschen also hier gerade Arbeitsspeicher gegen Arbeitsspeicher? Hast Du dafür jetzt eine sinnvolle Begründung, warum die Daten in 'ner Memorytable, 'nem TDirectory im Arbeitsspeicher sinnvoll sein könnten, in 'ner Query aber nicht? Und ich schrieb: Sinnvoll bei ein paar 100 Sätzen. Also: Wenig. Bei 10000den oder gar Millionen ist es natürlich absoluter Humbug mit Filtern zu arbeiten. Dann ist aber auch ein Locate völlig daneben und 'ne Memorytable und ein TDirectory ebenso. Ein Problem bei vielen Programmieren ist leider, dass sie die Leistungsfähigkeit von modernen Datenbanksystemen gnadenlos unterschätzen und deshalb mit viel Aufwand im Programm das nachbauen, was sie von der Datenbank selbst mit 'nem einfachen select mal eben performant geliefert bekommt. |
AW: Denkanstoß MemTable/Cachetable FireDac
Also grundsätzlich wird bisher immer ein SQL mit der entsprechenden Where-Bedingungn abgesetzt. Dies funktioniert auch schnell, aber der Mitarbeiter muss warten, also er startet das Prüfprogramm.
Und die ständige Abfrage dauert immer ca. 30 ms. Dies ist nicht die einzige Tabelle die Daten beinhaltet, die zur Prüfung notwendig sind. In der besagten Tabelle sind nur 10-50 Datensätze enthalten, diese in den Speichern zu nehmen und über den Filter einzugrenzen sehe ich als Peformanceverbesserung an. In einer weiteren Tabelle benfalls nur 10-20. |
AW: Denkanstoß MemTable/Cachetable FireDac
Also jetzt haben wir mal eine Mengenangabe.
Bei so kleinen Mengen ist ein Filter sinnvoll. Und wenn Du davon ein paar Tabellen hast, sehe ich keinen Grund, warum Du das nicht so machen solltest. |
AW: Denkanstoß MemTable/Cachetable FireDac
Perfekt, dickes Danke schön!
|
AW: Denkanstoß MemTable/Cachetable FireDac
Zitat:
Alternativ müsstest du dann eben dein SELECT so aufbauen, dass es pro Tag (oder Monat) einen Datensatz liefert, welcher sich per Locate oder Lookup anspringen liese. Selbst bei "Tagen" wären es in 100 Jahren aber auch nicht soooo viel Speicher, für die paar Datensätze. |
AW: Denkanstoß MemTable/Cachetable FireDac
Zitat:
Delphi-Quellcode:
DataSet.Locate('DatumVon;DatumBis', VarArrayOf([vDatumVon, vDatumBis]), []);
|
AW: Denkanstoß MemTable/Cachetable FireDac
Ja, mehrere Spalten prüfen, aber nicht mehrere Werte in einer Spalte, bzw. ein Wert zwischen zwei Spalten. :zwinker:
Delphi-Quellcode:
geht nicht.
// Datem zwischen Von und Bis
DataSet.Locate('Datum', VarArrayOf([DatumVon, DatumBis]), []); DataSet.Locate('DatumVon;DatumBis', Datum), []); Was aber beim Filter ginge
Delphi-Quellcode:
oder im Query.OnFilterRecord (Locate/Lookup mit einer Eventmethode wäre manchmal was Feines ... oder sowas wie im LINQ)
Query.Filter := Format('%0:d <= Datum and Datum <= %0:d', [2020, 2021]);
Query.Filter := Format('DatumVon <= %0:d and %0:d <= DatumBis', [2020]); Query.Filtered := True; if Query.RecordCount > 0 then |
AW: Denkanstoß MemTable/Cachetable FireDac
Jetzt muss ich doch nochmal Fragen, ich bekomme das mit den zwei Werten im Filter nicht hin.
Kennt jemand die genaue Syntax? |
AW: Denkanstoß MemTable/Cachetable FireDac
Zitat:
![]() |
AW: Denkanstoß MemTable/Cachetable FireDac
Danke für den Link. Hätte ich auch selber drauf kommen können. :-)
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:54 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