Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi Speichern eines großen Arrays (https://www.delphipraxis.net/149837-speichern-eines-grossen-arrays.html)

Shubit 31. Mär 2010 16:21


Speichern eines großen Arrays
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich versuche derzeit meinen Vokabeltrainer etwas zu optimieren. Dabei habe ich es besonders auf die Speicherung der Vokabeln abgesehen:
Delphi-Quellcode:
  TVocabulary = record
    Language1: String[100];
    Language2: String[100];
    Note: String[100];
    AddedOn: TDateTime;
    LastTraining: TDateTime;
    Chance: Single;
    Mistakes: Integer;
    Trials: Integer;
    Box: Integer;
  end;
Während das Programm läuft liegen alle Vokabeln in einem array of TVocabulary vor und werden beim schließen in einer File of TVocabulary gespeichert.
Das hat aus meiner Sicht folgende Vorteile:
1. Mit dem Array lässt es sich sehr gut arbeiten (man brauch kein seak, read oder write)
2. Durch das Anhängen eines zusätzlichen Datensatzes lassen sich leicht ein paar Einstellungen zur Vokabelliste speichern, sodass jede Datei eine eigenständige Vokabelliste darstellt, die man problemlos verschieben oder an Freunde schicken kann (mit den Einstellungen).
3. Da alle Daten zur Laufzeit im Arbeitsspeicher liegen, gehen auch aufwändigere Aktionen (statistische Daten) sehr schnell.
4. Keine Datenbankinstallation oder gar Datenbanktreiber notwendig. Die File of TVocabulary wird von einer selbst geschriebenen Klasse (hab sie auch mal angehangen) verwaltet

aber auch ein paar Nachteile:
1. Bei vielen Vokabeln (~100k) dauert das Laden in den Array und das Speichern in die Datei etwas länger (von der Darstellung in einer Listbox ganz zu schweigen^^)
2. Die File of TVocabulary benötigt eine Begrenzung (Finalization) für die Strings. Dies bedeutet das man sich endscheiden muss zwischen einer möglichst unbegrenzten Stringlänge (die 100 Zeichen ist schon unterste Schmerzgrenze) und keiner überflüssigen Arbeitsspeicherverschwendung, denn egal wie lang die Vokabel dann wirklich ist, wird Platz für 300 Zeichen reserviert.


Was ich nun suche ist eigentlich einen Weg, der die gleichen Vorteile bietet, aber auch die Nachteile deutlich minimiert. Und bevor ich wild drauf los programmiere, wollt ich einfach mal euren Rat hören bzw. eure Ideen hören.
Meine Idee wäre erstmal:
a) Die Klasse zur Verwaltung so anpassen, dass sie nur die aktuell ausgewählte Vokabel lädt und in der angesprochenden Listbox immer nur so 1000 Vokabeln dargestellt werden (muss man sich dann etwas Mühe mit dem Seak bei der File of TVocabulary damit man nicht verrutscht). Damit auch die Statistiken schnell dargestellt werden, könnte man dafür dann ein paar Variablen mitführen (z.B. immer den Index der Vokabel mit den meisten Fehlern in einem Integer speichern). Wär halt etwas Aufwand...
b) Datenbank, aber davon hab ich eigentlich keine Ahnung und ich möchte dem Benutzer keine Datenbankinstallation zumuten.

So, jetzt seid ihr dran :)

s.h.a.r.k 31. Mär 2010 16:26

Re: Speichern eines großen Arrays
 
Datenbanken wäre für so etwas sehr gut geeignet und kommen auch mit sehr großen Datenmengen klar. Wenn du nichts installieren willst, so brauchst du nur eine Embedded-Datenbank nutzen, SQLite wäre hier ein Beispiel.

Die Datensätze dann in einem Array zu halten, finde ich etwas unhandlich. Da gefallen mit TList (bzw. TObjectList) wesentlich besser, wobei ich aus dem Record eine Klasse gemacht hätte.

Shubit 31. Mär 2010 17:09

Re: Speichern eines großen Arrays
 
Wenn ich eine Datenbank habe, brauch ich ja keinen Array mehr. Dann hab ich noch 2 Fragen:
a) Also ich möchte das der Nutzer nur eine Datei pro Vokabelliste hat und keine zusätzlichen dll's. Mit welchen Komponenten/Datenbank geht das am besten?
b) Wo finde ich ein gutes Tutorial für SQLite in Delphi (2010)?
c) Wie wähle ich dann aus der Tabelle einen zufälligen Eintrag aus? (ich kann ja keine zufällige ID wählen, weil diese ja nicht existieren muss)

s.h.a.r.k 31. Mär 2010 19:30

Re: Speichern eines großen Arrays
 
Hier im Forum findest du eigentlich sehr viel über SQLite, ansonsten solltest du dir mal die Abfrage-Sprache SQL anschauen. Schwer zu verstehen ist diese eigentlich nicht. Findest hier im Forum Hilfen zu SQL-Problemen, aber wohl kein Tutorial. Hier findest ein paar, die teilweise auch auf SQLite anzuwenden sind (ZEOS).

Ich glaube nicht, dass man via SQL einen zufälligen Datensazu auswählen kann, aber hierzu eine Idee: Du selektierst alle IDs dazu (z.B.: SELECT UserID FROM Users) und liest anschließend die Anzahl der zurückgegeben Datensätze aus. Somit hast du die Obergrenze für die Zufallszahl, die du via Delphi generieren lassen kannst. Über diese Zahl selektierst du dann den Datensatz, was du über die IDs, die du ausgelesen hast, bewerkstelligen kannst.

// edit

zu a) wenn du eine Embedded-Datenbank nutzt, dann musst du immer eine DLL liefern inkl. der Datenbank-Datei(en) in der die Daten stehen. Das ist zumindest mein Stand der Dinge.

Shubit 1. Apr 2010 09:00

Re: Speichern eines großen Arrays
 
Na gut, über den zufälligen Datensatz mach ich mir dann nochmal Gedanken wenn der Rest läuft (wird schon irgendiwe gehen).

Ka ob es eine gute Endscheidung war, aber ich hab mich jetzt erstmal für die Zeos Komponenten und Sqlite endschieden.
Der Sql Syntax ist ja an ausreichend Stellen ganz gut erklärt (auch wenn die Anfürhungszeichen überall etwas anderes gehandhabt werden). Aber in Verbindung mit Delphi hapert es jetzt:

Delphi-Quellcode:
  ZConnection := TZConnection.Create(self);
  ZQuery := TZQuery.Create(self);
  ZTable := TZTable.Create(self);

  with ZConnection do
  begin
    Protocol := 'sqlite-3';
    Database := 'database.db';
    Connect;
  end;

  ZQuery.Connection := ZConnection;
  ZTable.Connection := ZConnection;

  with ZQuery do
  begin
    SQL.Clear;
    SQL.Add('CREATE TABLE IF NOT EXISTS VocabularyList (');
    SQL.Add('id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,');
    SQL.Add('Language1 VARCHAR(255) NULL,');
    SQL.Add('Article1 VARCHAR(7) NULL,');
    SQL.Add('Language2 VARCHAR(255) NULL,');
    SQL.Add('Article2 VARCHAR(7) NULL,');
    SQL.Add('Note VARCHAR(255) NULL,');
    SQL.Add('Chance DOUBLE NULL,');
    SQL.Add('AddedOn DOUBLE NULL,');
    SQL.Add('LastTraining DOUBLE NULL,');
    SQL.Add('Mistakes INTEGER NULL,');
    SQL.Add('Trials INTEGER NULL,');
    SQL.Add('Box INTEGER NULL');
    SQL.Add(');');

    ExecSQL;
  end;

  with ZQuery do
  begin
    SQL.Clear;
    SQL.Add('INSERT INTO VocabularyList (Language1, Article1, Language2, ' +
        'Article2, Note, Chance, AddedOn, LastTraining, Mistakes, Trials, Box) '
        + 'VALUES (:Language1, :Article1, :Language2, :Article2, :Note, ' +
        ':Chance, :AddedOn, :LastTraining, :Mistakes, :Trials, :Box)');
    ParamByName('Language1').AsString := 'Wort';
    ParamByName('Article1').AsString := 'das';
    ParamByName('Language2').AsString := 'word';
    ParamByName('Article2').AsString := 'the';
    ParamByName('Chance').AsFloat := 0.5;
    ParamByName('AddedOn').AsDateTime := Now;
    ParamByName('LastTraining').AsDateTime := Now;
    ParamByName('Mistakes').AsInteger := 0;
    ParamByName('Trials').AsInteger := 0;
    ParamByName('Box').AsInteger := 0;

    ExecSQL;
  end;

  with ZQuery do
  begin
    SQL.Clear;
    ZQuery.SQL.Add('SELECT * FROM VocabularyList');
  end;
Also das Erstellen der Tablle funktioniert noch, das Einfügen eines Datensatz scheitert an einen Komma-Syntax Fehler und die Abfrage zum Schluss sollte zwar funktionieren, dann hab wie greif ich dann auf die Abgefragten Daten zu (für die Ausgabe)?

s.h.a.r.k 1. Apr 2010 10:03

Re: Speichern eines großen Arrays
 
Beim Insert hast du ParamByName('Note') vergessen.

Und zum Select nutzt du kein ExecSQL, sondern ein Open und Close :zwinker:

PS: Du brauchst kein Clear und Add zu verwenden, wenn du sofort alles in Query.SQL.Text wirfst. (zumindest klappt das bei AnyDAC so, müsste bei ZEOS aber auch gehen)

Shubit 1. Apr 2010 10:16

Re: Speichern eines großen Arrays
 
Ok danke.

Hmm das mit dem Open hab ich schon gelesen, allerdings war das nicht so meine Frage (wobei die vielleicht schlecht gestellt war).
Als ich die File of TVocabulary hatte, wusste ich wie ich die in den array schreibe und dort dann auf die einzelnen Elemente und Eigenschaften zugreife und nun? Wie komm ich an die Daten ran, die ich die Datenbank gerade so sorgfältigt habe auslesen lassen?
ZQuery.SQL.Add('SELECT * FROM VocabularyList');
ZQuery.Open;
Wie kann ich die einzelnen Zeilen jetzt durchgehen und mir anschauen was in den Spalten drinsteht? Und wie nutzte ich den UPDATE Befehl zum Zurückschreiben von Änderungen?
Vielleicht steh ich auch gerade auch nur mächtig auf den Schlauch und ein kurzes Beispiel würde schon reichen.

Teekeks 1. Apr 2010 10:21

Re: Speichern eines großen Arrays
 
Zuerst mal ein First.
Dann mit ParambyName('Note').asstring.
usw.
Den nächsten Datensatz bekommst du mit Next;

DeddyH 1. Apr 2010 10:30

Re: Speichern eines großen Arrays
 
Auslesen:
Delphi-Quellcode:
DeineQuery.Close;
DeineQuery.SQL.Text := 'SELECT Feld1, Feld2 FROM Tabelle';
DeineQuery.Open;
while not DeineQuery.EOF do
  begin
    ShowMessage(DeineQuery.FieldByName('Feld1').AsString);
    ShowMessage(DeineQuery.FieldByName('Feld2').AsString);
    DeineQuery.Next;
  end;
Einfügen:
Delphi-Quellcode:
DeineQuery.Close;
DeineQuery.SQL.Text := 'INSERT INTO Tabelle(Feld1, Feld2) VALUES(:feld1,:feld2)';
DeineQuery.ParamByName('feld1').Value := wert1;
DeineQuery.ParamByName('feld2').Value := wert2;
DeineQuery.ExecSQL;
Aktualisieren:
Delphi-Quellcode:
DeineQuery.Close;
DeineQuery.SQL.Text := 'UPDATE Tabelle SET Feld1=:feld1, Feld2=:feld2 WHERE ID=:id';
DeineQuery.ParamByName('feld1').Value := wert1;
DeineQuery.ParamByName('feld2').Value := wert2;
DeineQuery.ParamByName('id').Value := PK_des_Datensatzes;
DeineQuery.ExecSQL;

Shubit 1. Apr 2010 11:03

Re: Speichern eines großen Arrays
 
@Teekeks: etwas sehr kurzes Beispiel, da ist mir das von DeddyH doch lieber :D

Zu dem Syntax Fehler:
Das geht:
Delphi-Quellcode:
    SQL.Add('INSERT INTO VocabularyList (Language1, Article1, Language2, ' +
        'Article2, Note, Chance, AddedOn, LastTraining, Mistakes, Trials, Box) '
        + 'VALUES (:Language1,:Article1,:Language2,:Article2,:Note,' +
        ':Chance,:AddedOn,:LastTraining,:Mistakes,:Trials,:Box);');
    ParamByName('Language1').Value := 'Wort';
    ParamByName('Article1').Value := 'das';
    ParamByName('Language2').Value := 'word';
    ParamByName('Article2').Value := 'the';
    ParamByName('Note').Value := 'Notiz';
    ParamByName('Chance').AsFloat := 0.5;
    ParamByName('AddedOn').AsDateTime := Now;
    ParamByName('LastTraining').AsDateTime := Now;
    ParamByName('Mistakes').AsInteger := 0;
    ParamByName('Trials').AsInteger := 0;
    ParamByName('Box').AsInteger := 0;
    ExecSQL;
Das geht nicht:
Delphi-Quellcode:
    SQL.Add('INSERT INTO VocabularyList (Language1, Article1, Language2, ' +
        'Article2, Note, Chance, AddedOn, LastTraining, Mistakes, Trials, Box) '
        + 'VALUES (:Language1,:Article1,:Language2,:Article2,:Note,' +
        ':Chance,:AddedOn,:LastTraining,:Mistakes,:Trials,:Box);');
    ParamByName('Language1').AsString := 'Wort';
    ParamByName('Article1').AsString := 'das';
    ParamByName('Language2').AsString := 'word';
    ParamByName('Article2').AsString := 'the';
    ParamByName('Note').AsString := 'Notiz';
    ParamByName('Chance').AsFloat := 0.5;
    ParamByName('AddedOn').AsDateTime := Now;
    ParamByName('LastTraining').AsDateTime := Now;
    ParamByName('Mistakes').AsInteger := 0;
    ParamByName('Trials').AsInteger := 0;
    ParamByName('Box').AsInteger := 0;
    ExecSQL;
Scheinbar funktioniert die AsString procedure nicht, weshalb die Datenbank dann meckert das da 5 Kommas ohne Strings dazwischen sind.



Zufälliger Eintrag:
a) Array mit allen gültigen ID's erstellen und über zufälligen Index des Arrays ...
b) Alle Zeilen aus der Datenbank holen und dann eine zufällige Anzahl an Schritten weiter gehen. gibt es sowas wie ZQuery.Count (Anzahl der Zeilen)?
c) Ich denke das macht man zwar eher nicht, aber man könnte ja auch beim Löschen eines Eintrages alle größeren ID's anpassen und dann hätte man direkt eine zufällige ID nehmen, weil man wüsste das alle ID's von 1 bis zur Anzahl Zeilen vergeben sind.

Ich brauch den zufälligen Eintrag für das Vokabeltraining, derzeit wird das ganze so ermittelt:
Delphi-Quellcode:
        repeat
          mNeuerIndex := random(Length(mVokabelListe));
          x := random;
        until (x < mVokabelListe[mNeuerIndex].Chance) and
          (mNeuerIndex <> mLetzterIndex);
(die Chance gibt also die Wahrscheinlichkeit an mit der die Vokabel ausgewählt wird)
Cool wär halt: 'SELECT * FROM VokabelListe WHERE Chance > :Zufallszahl AMD id <=> :LetzteID AMD id = .ZufaelligeID'


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:25 Uhr.
Seite 1 von 3  1 23      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz