AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Delphi Speichern eines großen Arrays
Thema durchsuchen
Ansicht
Themen-Optionen

Speichern eines großen Arrays

Ein Thema von Shubit · begonnen am 31. Mär 2010 · letzter Beitrag vom 13. Apr 2010
Antwort Antwort
Seite 1 von 3  1 23      
Shubit

Registriert seit: 17. Mai 2009
64 Beiträge
 
#1

Speichern eines großen Arrays

  Alt 31. Mär 2010, 16:21
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
Angehängte Dateien
Dateityp: pas mvocabularybook_177.pas (40,1 KB, 7x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#2

Re: Speichern eines großen Arrays

  Alt 31. Mär 2010, 16:26
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.
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Shubit

Registriert seit: 17. Mai 2009
64 Beiträge
 
#3

Re: Speichern eines großen Arrays

  Alt 31. Mär 2010, 17:09
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)
  Mit Zitat antworten Zitat
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#4

Re: Speichern eines großen Arrays

  Alt 31. Mär 2010, 19:30
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.
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Shubit

Registriert seit: 17. Mai 2009
64 Beiträge
 
#5

Re: Speichern eines großen Arrays

  Alt 1. Apr 2010, 09:00
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)?
  Mit Zitat antworten Zitat
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#6

Re: Speichern eines großen Arrays

  Alt 1. Apr 2010, 10:03
Beim Insert hast du ParamByName('Note') vergessen.

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

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)
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Shubit

Registriert seit: 17. Mai 2009
64 Beiträge
 
#7

Re: Speichern eines großen Arrays

  Alt 1. Apr 2010, 10:16
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.
  Mit Zitat antworten Zitat
Teekeks

Registriert seit: 19. Okt 2008
Ort: Dresden
765 Beiträge
 
FreePascal / Lazarus
 
#8

Re: Speichern eines großen Arrays

  Alt 1. Apr 2010, 10:21
Zuerst mal ein First.
Dann mit ParambyName('Note').asstring.
usw.
Den nächsten Datensatz bekommst du mit Next;
Peter
"Div by zero Error" oder auch: "Es geht auch ohne Signatur!".
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.542 Beiträge
 
Delphi 11 Alexandria
 
#9

Re: Speichern eines großen Arrays

  Alt 1. Apr 2010, 10:30
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;
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Shubit

Registriert seit: 17. Mai 2009
64 Beiträge
 
#10

Re: Speichern eines großen Arrays

  Alt 1. Apr 2010, 11:03
@Teekeks: etwas sehr kurzes Beispiel, da ist mir das von DeddyH doch lieber

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'
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 3  1 23      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:33 Uhr.
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