![]() |
Textdatei > 500 MB für DB vorbereiten
Hallo zusammen,
ich habe hier eine Textdatei die ca. 180.000 Datensätze enthält. Diese Datensätze müssen nun in eine Access.db überführt werden. Die Ausgangsdatei ist folgendermaßen aufgebaut:
Code:
Wie man sieht, ist nicht jeder Datensatz identisch aufgebaut, will sagen in einem Datensatz können es 10 Felder sein, in einem anderen 20 (realistische Werte) sein.
Feldname 1: Feldinhalt
Feldname 2: Feldinhalt Feldname 3: Feldinhalt Feldname 4: Feldinhalt Feldname 5: Feldinhalt Feldname 6: Feldinhalt ... Feldname n: Feldinhalt Datensatztrenner (0x0c) Feldname 1: Feldinhalt Feldname 3: Feldinhalt Feldname 4: Feldinhalt Feldname 6: Feldinhalt ... Feldname n: Feldinhalt Datensatztrenner (0x0c) usw. Die Access-DB muss aus den Feldern (ALLE möglichen) generiert und anschließend befüllt werden. Das ganze soll evtl. im Quartalsrythmus aktualisiert werden. Mein Problem: Mir fällt im Augenblick nichts effizientes (Speicherbedarf, Geschwindigkeit) ein, um die Datei zu analysieren / vorzubereiten bzw. in Access zu überführen. Für Denkanstösse wäre ich sehr dankbar. |
Re: Textdatei > 500 MB für DB vorbereiten
Ist das ein Lotus Textformat?
Ich hab mal für sowas Ähnliches(Lotus Text, oder so) einen Konverter zu CSV geschrieben und es da so gemacht, dass ich erst einen Durchgang gemacht habe um alle Felder zu bestimmen und diese dann in nen Array geschrieben hab. Dann hab ich die Feldnamen schonmal in die CSV geschrieben. Als nächstes bin ich dann das ganze Datensatz für Datensatz durchgangen und ich hab das dann auch wieder in einen zweiten Array geschrieben, der die gleiche Anzahl von Elementen wie der Erste hat und das dann an die gleiche Position, die der entsprechende Feldname im ersten Array hat, geschrieben. Am Datensatz-Ende wurde dieser Array dann in die CSV geschrieben (Du müsstest das natürlich in die Datenbank schreiben). Hoffe dir hilft das ein bissle, wenns dich interessiert kann ich die Source von dem Programm raussuchen, bin mir aber nicht ganz sicher, ob ich die finde. |
Re: Textdatei > 500 MB für DB vorbereiten
Ich würde die Datei per Stream in einen Buffer laden (immer einen Datensatz) und diesen dann zerlegen und in die Datenbank schreiben.
|
Re: Textdatei > 500 MB für DB vorbereiten
@HalloDu: Ja, es ist ein LoNo Export und als Lösungsansatz wäre ich schon an Deinem Source intressiert.
@mkinzler: So in etwa habe ich schon angefangen, aber das dauert ziemlich lange. Trotzdem Dank, denn das zeigt, das ich nicht total auf dem Holzweg war... |
Re: Textdatei > 500 MB für DB vorbereiten
was ist denn "ziemlich lange" für eine Zeiteinheit?
(dat ganze sollte in wenigen Minütchen zu bewerkstelligen sein ... kommt auf die DB an, aber unter 5, wenn nicht gar unter 2 minuten wär schon möglich) |
Re: Textdatei > 500 MB für DB vorbereiten
Liste der Anhänge anzeigen (Anzahl: 1)
Hier sind die Sourcen+compilierte Exe. Sind allerdings nur sporadisch kommentiert und auch überhaupt nicht optimiert (Hab das mal nur so zwischendurch geschrieben). Aber sie funktionieren einwandfrei.
|
Re: Textdatei > 500 MB für DB vorbereiten
Zitat:
Derzeit liege ich beim Suchdurchlauf (Auslesen der Feldnamen) bei deutlich über fünf Minuten und da ist noch keine Zuordnung der Feldinhalte, geschweige denn das schreiben der DB enthalten :cry: Gibst da vielleicht irgendein Trick :gruebel: @halloDu: Danke, schau ich mir nachher gleich mal an. |
Re: Textdatei > 500 MB für DB vorbereiten
Ohne die Datei und deinen Code zu kennen, kann man da nicht viel sagen.
Poste doch mal den Code und den Anfang der Datei (ein paar DS) |
Re: Textdatei > 500 MB für DB vorbereiten
also das läßt sich leicht ausrechnen ... hier auf dem "altem" Rechner jag ich ich mal so 10 MB/Sekunde durch die Platte und daheim weit über 30)
500MB / 10MB/s = 50s und dazu noch kommt dann noch das rumstöbern in den gelesenen Daten. Wie mkinzler schon sagte, was hast du denn bis jetzt ... mal sehn was sich optimieren läßt. OK, maximal Geschwindigkeit muß ja bestimmt nicht unbedingt sein ... würde ja (eventuell sehr viel) mehr Aufwand bedeuten. |
Re: Textdatei > 500 MB für DB vorbereiten
Liste der Anhänge anzeigen (Anzahl: 1)
Ok, im Anhang ein ausführlicheres Beispiel der Datensätze (Daten sind angepasst, Originaldaten kann ich leider nicht veröffentlichen).
Zu Testzwecken (ich hatte in etwa an den von HalloDu beschriebenen Ansatz gedacht, also erstmal Feldnamen holen und dann Daten zuordnen) habe ich ![]() Hier der verwendete erste Versuch (angepasst aus dem o.g. TATStreamSearch-Demo):
Delphi-Quellcode:
Der Aufruf erfolgt durch:
function TfrmMain.Find(AFindFirst: Boolean):Boolean;
var OK: Boolean; begin Result := False; OK := False; try if AFindFirst then begin try strsrch_Main.FileName := FFileName; except Application.MessageBox('Cannot open filename specified', 'Error', MB_OK or MB_ICONERROR); Exit; end; OK := strsrch_Main.FindFirst('(\w*): ', 0, FEncoding, FOptions); end else begin OK := strsrch_Main.FindNext; end; except on E: Exception do if E.Message <> '' then begin Application.MessageBox(PChar(E.Message), 'Search failed', MB_OK or MB_ICONERROR); Exit; end; end; if OK then begin bh_Main.SetSelection(strsrch_Main.FoundStart, strsrch_Main.FoundLength, True); Inc(FRecordCount); memUC_Main.Lines.Add(bh_Main.SelText); edOffset.Text := IntToStr(strsrch_Main.FoundStart); edCount.Text := IntToStr(FRecordCount); pb1.Position := strsrch_Main.FoundStart * 100 div FFileSize; end else Application.MessageBox('Search string not found', 'Search failed', MB_OK or MB_ICONERROR); // Application.ProcessMessages; result := OK; end;
Delphi-Quellcode:
Wie gesagt, das war erst einmal nur ein Test um ein Gefühl für die Zeit zu bekommen, die das Auslesen benötigen könnte.
procedure TfrmMain.btnUC_GetKeysClick(Sender: TObject);
var OK:Boolean; begin FAbort := False; FEncoding := vencANSI; FOptions := []; FRecordCount := 0; Include(FOptions, asoRegEx); if Find(True) then repeat OK := Find(False); until (not OK) or FAbort; FAbort := False; end; |
Re: Textdatei > 500 MB für DB vorbereiten
das #12 ist da immer in einer eigenen Zeile?
(oder maximal am Ende einer Datensatzzeile, also vor einem Zeilenwechsel bzw hinter einem Feldnamen) zwei ganz einfache Beispiele zum Auslesen aller Feldnamen: ! Groß-/Kleinschreibung wird beachtet
Delphi-Quellcode:
var F: TextFile;
Feldnamen: Array of String; S: String; B: Boolean; i: Integer; begin AssignFile(F, {Dateiname}); Reset(F1); Feldnamen := nil; while not EOF(F) do begin ReadLn(F, S); i := Pos(':', S); if i = 0 then Continue; S := Trim(Copy(S, 1, i - 1)); B := True; for i := High(Feldnamen) downto 0 do if Feldnamen[i] = S then B := False; if B then begin i := Length(Feldnamen); SetLength(Feldnamen, i + 1); Feldnamen[i] := S; end; end; CloseFile(F); end;
Delphi-Quellcode:
var F: TextFile;
Feldnamen: TStringList; S: String; i: Integer; begin AssignFile(F, {Dateiname}); Reset(F1); Feldnamen := TStringList.Create; Feldnamen.Duplicates := dupIgnore; while not EOF(F) do begin ReadLn(F, S); i := Pos(':', S); if i = 0 then Continue; S := Trim(Copy(S, 1, i - 1)); Feldnamen.Add(S); end; end; CloseFile(F); end; |
Re: Textdatei > 500 MB für DB vorbereiten
Aber das ist doch popeleinfach...
Delphi-Quellcode:
Die Funktion 'GetNextField' liest die nächste nicht-leere Zeile vom MyTextFile ein und liefert TRUE, wenn es sich um einen Feldinhalt handelt (und dann in 'sFieldName' den Feldnamen und in 'sFieldContents' den Inhalt), oder FALSE sonst.
While not MyTextFile.Eof do begin
MyTable.Append; While GetNextField(MyTextFile, sFieldName, sFieldContents) do MyTable[sFieldName].AsString := sFieldContents MyTable.Post; End; Das Langsame hier ist:
Delphi-Quellcode:
Das bekommt man mit einer kleinen Hashmap wesentlich schneller hin. Diese Map liefert zum Feldnamen den Feldindex, sodaß man dann direkt in die entsprechende Spalte schreiben kann.
MyTable[sFieldName].AsString := sFieldContents
Selbst wenn das Einlesen 30 min dauert, ist das immer noch kürzer, als umständlich nach einer performanten Lösung zu suchen. Was hast Du davon, wenn Du in 5 Tagen eine solche implementiert hast? [edit]... Wenn die Tabelle noch nicht existiert bzw. die Gesamtmenge der Feldnamen noch nicht bekannt ist, würde ich die Datei einmal durchscannen, um alle Feldnamen zu erhalten. Damit wird dann die Tabelle erzeugt bzw. ggf. erweitert. Im laufenden Betrieb geht das nicht so einfach. Du kannst aber auch komplett auf ein TDataset verzichten und nur mit SQL arbeiten. Dazu müsste man aber wissen, ob Access auch DDL kennt, also CREATE / ALTER TABLE, um ggf neue Feldnamen an die Tabelle anzubepseln. [/edit] |
Re: Textdatei > 500 MB für DB vorbereiten
Zitat:
Die Zeile wird durch CRLF abgeschlossen. Der Datensatz wird durch mindestens eine Leerzeile gefolgt von 0x0c in einer extra Zeile vom nächsten Datensatz getrennt. @himitsu: Beide Deiner Routinen sind im Prinzip schnell (auf meinem Testrechner so um die 40 - 50 Sekunden) :thumb: , ABER damit ist das Prog optisch eingefroren. :( Füge ich nun ein Application.ProcessMessage hinzu um z.B. die FilePos oder einen Fortschrittsbalken anzuzeigen, bricht die Performance total zusammen und ich liege in etwa bei der Zeit wie mit meiner kleinen Testroutine. (na ja stimmt nicht ganz, deine ist immer noch deutlich schneller :thumb: ) :( Also werde ich in dieser Richtung mal weitertesten und mich von Streams (mein Ansatz) fernhalten... @alzaimar: Zitat:
Zitat:
Zitat:
Zitat:
|
Re: Textdatei > 500 MB für DB vorbereiten
ich gehe mal davon aus, dass du nicht alle felder abspeichern willst (z. B. SystemABDocUnID: HexID) und wohl auch nicht alle daten, die in dem 1/4 geändert wurden verwerfen.
dann müsstest du dir erst mal aussuchen, welche daten du überhaupt den anwender zeigen willst und in welcher tabellenstruktur du das ablegen möchtest. wenn du das gemacht hast, ist die hauptarbeit eigentlich schon erledigt. du kennst die felder, die tabellen und die namen aus Lotus notes. dann bruchst nur noch einmal durchgehen, wenn der name auftritt entsprechend zuweisen und einmal abspeichern. das geht ruck-zuck. ggf. müsstest du da noch prüfen ob die einträge schon vorhanden sind nach dem schema if not exists then insert else update. das endekennzeichen ist ja #12 und von daher weisst prima wenn 'n neuer datensatz beginnt. schon mal darüber nachgedacht? |
Re: Textdatei > 500 MB für DB vorbereiten
Zitat:
Zitat:
Zitat:
Zitat:
Ein Delta einspielen oder einfach mit dem Gesamtabzug überschreiben. :gruebel: |
Re: Textdatei > 500 MB für DB vorbereiten
der gesamtabzug mit einem vorherigen drop-table ist mit sicherheit schneller. die frage ist nur, ob du das wirklich willst...
ansonsten, sind die benutzerdefinierten felder normal in der lokalen db, nicht in der server db. wie siehts denn aus, woher kommen die daten... und wie sollen sie eingespielt werden... jeder benutzer spielt im quartal das selbe ein? find ich persönlich nicht wirklich sinnvoll. ausserdem trau ich den wenigsten usern zu, dass sie etwas sinnvolles auswählen... wäre es da nicht sinnvoller die daten zentral einzuspielen mit einem superset an daten, die für alle gleich sind und dann die einzelnen user dann die daten zusammenstreichen lassen, welche sie gerne sehen würden? PS: zum einspielen gibt es ja auch mehrere möglichkeiten, z.b. erst mal 'ne tabelle mit 60 oder noch mehr feldern aufmachen, die namen dann zum schluss assigen und die restliche dann zu droppen oder unbenutzt stehen zu lassen, oder einfach alles in 'n blob übernehmen ... ;-) ausserdem, wenn du richtig hingesehen hast, hast du in deinem beispiel flatfile wiederholungszeilen. wie willst mit dennen umgehen ... auch alles 'in 'n blob oder so, dass man danach suchen kann? im zweiten fall, brauchst du 'ne detailtabelle, damit du das ordentlich abhandeln kannst. hoffe solche kleinigkeiten hast nicht übersehen und sind bereites in deinem konzept integriert. |
Re: Textdatei > 500 MB für DB vorbereiten
Zitat:
Zitat:
Hmmm. Du sagst, Du hättest 180.000 Datensätze mit jeweils ca. 20-40 Feldern. Also hmmm, ich denke, das geht in 1-2 Minuten (Feldnamen ermitteln). Was ich daran so dämlich finde, ist jedoch, das die Gesamtmenge der Feldnamen nicht definiert sind. Nun gut. Mal sehen, wenn mir morgen langweilig ist, setz ich mich mal ran. |
Re: Textdatei > 500 MB für DB vorbereiten
Zitat:
Dein TStringDictionary schaue ich mir mal morgen (upps schon so spät) heut nachmittag an. Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
|
Re: Textdatei > 500 MB für DB vorbereiten
Zitat:
was mir schleierhaft ist, was das progy machen soll, wenn der inhalt der db nicht defniert ist... taugt das ja noch nicht mal als adressliste... @alzaimar: was steht dagegen 'ne kontenform zu verwendeten, so wie der textoutput... |
Re: Textdatei > 500 MB für DB vorbereiten
Zitat:
ausserdem müssten natürlich noch entsprechende plausi prüfungen erfolgen. z.b. welche felder unbedingt gefüllt werden müssen und welche leer sein dürfen .. denke, alles in allen, ist das kein ad-hoc projekt. welches in 'ner 1/2 stunde eingeführt ist... da sollt man schon etwas drüber nachdenken, wie man das am sinnvollsten macht. so dass man das dann nicht jedes quartal umschreiben darf ... :angel: |
Re: Textdatei > 500 MB für DB vorbereiten
Zitat:
Es ist aber grauenvoll, sich vorzustellen, das Anwender A 'Ziel', Anwender B 'Target' und Anwender C 'Destination' für semantisch identischen Inhalt verwenden. Daraus würden dann drei unterschiedliche Spalten. Klasse. Zitat:
Gegen die Performanceeinbuße infolge der notwendigen Visualisierung hilft ein einfaches 'Zeig den Fortschritt nur alle 1000 Datensätze'. Das entspräche einer Aktualisierung der Anzeige 1x pro Sekunde (ca.). Das sollte reichen. Muss das eigentlich alle paar Monate stattfinden? wäre es nicht sinnvoll, bei derartigen Datenmengen eine tägliche Synchronisation durchzuführen? |
Re: Textdatei > 500 MB für DB vorbereiten
Zitat:
Zitat:
Zitat:
Das ganze soll an Aussendienstmitarbeiter / Aussenstellen gehen, die über keine Breitbandverbindung verfügen UND auch kein LoNo haben. Deswegen der Ansatz mit Access, denn das haben alle auf'm Rechner. Je nach weiterer Entwicklung dieser Geschichte könnte es dann doch bei den späteren Updates auf ein Delta hinauslaufen... Was definitiv nicht geht ist ein tägliches oder auch wöchentliches Update (Ich bin viel unterwegs und auch die Aussenstellen sind nicht ständig erreichbar). Daher der erst einmal angepeilte Quartalsrythmus. Die allerersten Gehversuche waren übrigens in VBA (Direkt Import in Access), was ich dann aber sehr schnell verworfen habe (Jahre später, Staub, Tote Fliegen, Spinnweben: oh es sind 10 % geschafft :zwinker: ). Das mit den Knoten (Baumdarstellung / TreeView???) ist eine Sache an die ich noch gar nicht gedacht bzw. in die engere Wahl gefasst habe (Manchmal denkt man einfach zu eingleisig). Zitat:
Entscheidend ist, das ich ein paar Denkanstösse erhalten habe und nun in diesen Richtungen testen werde. Ich habe auch schon überlegt die Datei aufzusplitten und in mehreren Threads zu analysieren, das dürfte aber auf den zweiten Blick keine entscheidenden Vorteile bringen. Was die Auswahl/Anzeigemöglichkeit des Imports betrifft, so habe ich mich entschlossen selbst festzulegen welche Felder relevant sein könnten und welche nicht. Der Nutzer muss dann damit leben. Ansonsten würde es wohl nur ein heilloses Chaos geben. Also, erst mal Dank an Alle für Unterstützung und Denkanstösse. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:42 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