Neuigkeiten:

Wenn ihr euch für eine gute Antwort bedanken möchtet, im entsprechenden Posting einfach den Knopf "sag Danke" drücken!

Mobiles Hauptmenü

CSV Zeichenfolge zerlegen, bei einem Datensatz Abbruch

Begonnen von stacc, Juni 26, 2019, 14:51:33

⏪ vorheriges - nächstes ⏩

stacc

Hallo Forum,

folgender Feldinhalt soll zerlegt werden (Die Daten stammen ursprünglich aus einem Scanner):
Bei diesem Datensatz klappt es nicht:
"5Z000","M3050",1,05.06.19 11:05:41,"INV"
Bei Übernahme des Datums gibt es einen Abbruch kommt Laufzeitfehler 5 ungültiger Prozeduraufruf oder ungültiges Argument (Access 2013)

Wird aber der dritte Wert (Mengenangabe) z. B. gegen eine 0 oder 2 ausgetauscht funktioniert alles:
"5Z000","M3050",0,05.06.19 11:05:41,"INV" oder
"5Z000","M3050",2,05.06.19 11:05:41,"INV"

Es funktioniert auch, wenn das Datum um eine Sekunde verändert wird also
"5Z000","M3050",1,05.06.19 11:05:42,"INV"

Für mich ist leider keine Grund erkennbar hier der Code:

  For I = 1 To Anzahl
       
        strScannerzeilentext = MeineDG!Scannerzeilentext  ' "567BB","M214",390,05.06.19 10:51:33,"INV"
        Pos_Komma = InStr(strScannerzeilentext, ",")
        strAbzug = Left(strScannerzeilentext, Pos_Komma)   ' "567BB",
       
        strOrt = Left(strAbzug, Pos_Komma - 1)          'Dann sind noch die " drin
        strOrt = Replace(strOrt, Chr(34), vbNullString) 'Hochzeichen werden entfernt
               
        strScannerzeilentext = Replace(strScannerzeilentext, strAbzug, vbNullString) '   "M214",390,05.06.19 10:51:33,"INV"
        Pos_Komma = InStr(strScannerzeilentext, ",")
        strAbzug = Left(strScannerzeilentext, Pos_Komma)  '    "M214",
        strMNr = Left(strAbzug, Pos_Komma - 1)
        strMNr = Replace(strMNr, Chr(34), vbNullString)   '     M214
        strMNr = Replace(strMNr, "M", vbNullString)   '     M214
       
        strScannerzeilentext = Replace(strScannerzeilentext, strAbzug, vbNullString) '  390,05.06.19 10:51:33,"INV"
        Pos_Komma = InStr(strScannerzeilentext, ",")
        strAbzug = Left(strScannerzeilentext, Pos_Komma)  '    390,
        lngMenge = Left(strAbzug, Pos_Komma - 1)
       
        strScannerzeilentext = Replace(strScannerzeilentext, strAbzug, vbNullString) '     05.06.19 10:51:33,"INV"
        Pos_Komma = InStr(strScannerzeilentext, ",")
        strAbzug = Left(strScannerzeilentext, Pos_Komma)  '    05.06.19 10:51:33,
       
        dateZaehlDatum = Left(strAbzug, Pos_Komma - 1)
           
        MeineDG.Edit
            MeineDG!Ort = strOrt
            MeineDG!MNr = strMNr
            MeineDG!Menge = lngMenge
            MeineDG!ZaehlDatum = dateZaehlDatum
        MeineDG.Update
       
        MeineDG.MoveNext
   
    Next I
 


Hoffe sehr auf Eure Hilfe - Datenbank zum Probieren anbei!

VG
Stefan

markusxy

#1
Hallo Stefan,
ich habs mir jetzt nicht angetan den Fehler zu suchen, sondern hab den Code etwas vereinfacht:

Sub splitten()
  Dim Data As Variant
 
  With CurrentDb.OpenRecordset("tbl_Scannerdaten_Pruefung_2", dbOpenDynaset)
    Do While Not .EOF
     
      Data = Split(!Scannerzeilentext, ",")
      .Edit
      !Ort = Mid$(Data(0), 2, Len(Data(0)) - 2)
      !MNr = Mid$(Data(1), 2, Len(Data(1)) - 2)
      !Menge = CInt(Data(2))
      !ZaehlDatum = Data(3)
      .Update
      .MoveNext
    Loop
    .Close
  End With
End Sub


Das Arbeiten mit Split ist natürlich sehr bequem dafür aber etwas ineffizient.
Bei mehr als 10000 Datensätzen würde ich auch mit instr arbeiten und dann mittels Mid zuweisen, das wäre dann um ein vielfaches schneller.
Aber so wie in deinem Beispiel wäre es auch langsam, da viele unnötige Schritte vorhanden sind.
Bei kleinen Datenmengen ist das aber wurscht.

stacc

Hallo markus888,
sehr elegant. Insbesondere weil die Werte unterschiedlich lang sind und Du mir
die Lösung gleich mit geliefert hast. Habe die Funktion auf Anhieb nur mit festen Werten gefunden.
Die Menge kann 6-stellig werden daher habe ich CLng eingesetzt
(Hat natürlich erst einen Überlauf gegeben und ich musste etwas forschen).
Habe einiges dazu gelernt - Danke!
Mit Deinem Code werden die Datensätze auch brav abgearbeitet, 987 in weniger als 1 Sekunde.
Zu meinem ursprünglichen Problem kann ich mittlerweile sagen,
das bei der Datumsauswertung einmal bei der Menge
0:  es zu diesem Ausdruck kommt zu [05.06.19 11:05:41],"INV"]
1: hier bei der Menge 1 kommt [05.06.19 11:05:4"INV"] raus
[] sind von mir ergänzt
Die Sekunde verliert eine Stelle und das Komma ist auch weg - kann nix geben!
Das kann doch nur ein ,,internes" Access-Problem sein, oder? Zumal wenn ich
aus 41 Sekunden 42 mache auch alles geht!
VG
Stefan
P. S. Wenn jemand Interesse hat, lade ich auch gerne die Datenbank ungepackt hoch (732kb),
von einem Newbie würde ich mir auch keine Zip-Datei runterladen ;-)
Ist allerdings accdb die sich nicht in mdb umwandeln lässt. Geht dann glaube ich auch nicht.

MzKlMu

Hallo,
eine ACCDB musst Du packen (Zip), das geht nicht anders. ACCDB ist zum Hochladen nicht zugelassen.
Gruß Klaus

ebs17

#4
Eine CSV, die ihren Namen so verdient, würde ich über eine Importspezifikation importieren/verknüpfen und auf das VBA-Gehoppel mit der Aufteilerei verzichten. Da entsteht schon mal weniger Datenmüll, und der Zugriff ist datentypsicher und direkter.
Mit freundlichem Glück Auf!

Eberhard

PhilS

Zitat von: stacc am Juni 27, 2019, 17:05:57
Zu meinem ursprünglichen Problem kann ich mittlerweile sagen,
das bei der Datumsauswertung einmal bei der Menge
0:  es zu diesem Ausdruck kommt zu [05.06.19 11:05:41],“INV“]
1: hier bei der Menge 1 kommt [05.06.19 11:05:4“INV“] raus
[] sind von mir ergänzt
Die Sekunde verliert eine Stelle und das Komma ist auch weg - kann nix geben!
Das kann doch nur ein „internes“ Access-Problem sein, oder? Zumal wenn ich
aus 41 Sekunden 42 mache auch alles geht!
Anstelle über interne Access-Probleme zu sinnieren, würde ich empfehlen lieber den eigenen Code kritisch anzuschauen.

Bei deinem Fall mit Menge=1 wird in der ersten, nachfolgend zitierten Codezeile der String 1, in die Variable strAbzug eingelesen. Das ist dann ein recht allgemein gehaltenes Suchkriterium für ein Replace...

strAbzug = Left(strScannerzeilentext, Pos_Komma)  '   
lngMenge = Left(strAbzug, Pos_Komma - 1)
       
strScannerzeilentext = Replace(strScannerzeilentext, strAbzug, vbNullString)



Neue Videoserie: Windows API in VBA

Klassische CommandBars visuell bearbeiten: Access DevTools CommandBar Editor

stacc

Hallo PhilS,
danke - werde zukünftig selbstkritischer sein!
Ich konnte dann das Problem ,1 mit , , 1 lösen.

Zitatmarkus888
Bei mehr als 10000 Datensätzen würde ich auch mit instr arbeiten und dann mittels Mid zuweisen, das wäre dann um ein vielfaches schneller.
Aber so wie in deinem Beispiel wäre es auch langsam, da viele unnötige Schritte vorhanden sind.

Bei 1 Mio. Datensätzen benötigt mein optimierter Code 48 s, die Variante von markus888 41 s.
Split scheint besser zu sein, oder kann ich bei meinem Code noch was rausholen?

Private Sub Befehl11_Click()
' Funktionen Instr und Mid

Me!F_ww_Scanner_Roh_Test_UF.SourceObject = "F_ww_Scanner_Roh_Test_UF"

    Dim AktuelleDB As DAO.Database
    Dim Pos_Komma As Integer
    Dim strScannerzeilentext As String
    Dim strAbzug As String

    Set AktuelleDB = CurrentDb
   
    With AktuelleDB.OpenRecordset("tbl_Scannerdaten_Pruefung_2", dbOpenDynaset)
   
    Do While Not .EOF
     .Edit
            strScannerzeilentext = !Scannerzeilentext  ' "567BB","M214",390,05.06.19 10:51:33,"INV"
            Pos_Komma = InStr(strScannerzeilentext, ",")
            strAbzug = Left(strScannerzeilentext, Pos_Komma)   ' "567BB",
        !Ort = Mid$(strAbzug, 2, Len(strAbzug) - 3)      '
            strScannerzeilentext = Replace(strScannerzeilentext, strAbzug, vbNullString, , 1) '   "M214",390,05.06.19 10:51:33,"INV"
            Pos_Komma = InStr(strScannerzeilentext, ",")
            strAbzug = Left(strScannerzeilentext, Pos_Komma)  '    "M214",
        !MNr = Mid$(strAbzug, 3, Len(strAbzug) - 4)     '    214
            strScannerzeilentext = Replace(strScannerzeilentext, strAbzug, vbNullString, , 1) '  390,05.06.19 10:51:33,"INV"
            Pos_Komma = InStr(strScannerzeilentext, ",")
            strAbzug = Left(strScannerzeilentext, Pos_Komma)  '    390,
        !Menge = CLng(Left(strAbzug, Pos_Komma - 1))
            strScannerzeilentext = Replace(strScannerzeilentext, strAbzug, vbNullString, , 1) '     05.06.19 10:51:33,"INV"
        !ZaehlDatum = Left(strScannerzeilentext, 17)
    .Update
    .MoveNext
    Loop
    .Close
    End With
       
    Me!F_ww_Scanner_Roh_Test_UF.SourceObject = "F_ww_Scanner_Roh_Test_UF_3"

End Sub


Zitat von: ebs17 am Juni 27, 2019, 19:49:10
Eine CSV, die ihren Namen so verdient, würde ich über eine Importspezifikation importieren/verknüpfen und auf das VBA-Gehoppel mit der Aufteilerei verzichten. Da entsteht schon mal weniger Datenmüll, und der Zugriff ist datentypsicher und direkter.

Die Daten einzulesen ist regelmäßig erforderlich, das soll auch ein ganz Unwissender per Button erledigen können. Ich will nicht immer alles selber machen müssen!

Zitat von: MzKlMu am Juni 27, 2019, 17:50:21
Hallo,
eine ACCDB musst Du packen (Zip), das geht nicht anders. ACCDB ist zum Hochladen nicht zugelassen.

Kapiert, accdb ist nicht direkt erlaubt aber so geht es natürlich!

Danke an Euch alle!
Auf einen Codeverbesserungsvorschlag würde ich mich noch freuen. Split scheint aber top zu sein.

VG
Stefan







MzKlMu

Hallo,
ZitatDie Daten einzulesen ist regelmäßig erforderlich, das soll auch ein ganz Unwissender per Button erledigen können. Ich will nicht immer alles selber machen müssen!
wenn das eine ganz klassische CSV ist, kann man (einmalig) eine Importspezifikation anlegen. In der Importspezifikation lassen sich alle möglichen Anpassungen vornehmen, auch Datentypen. Die Importzpazi kann dann gespeichert werden. Deren Name wird dann im Code verwendet. Auch dann läuft der Code völlig automatisch ab, da ist nix weiter mehr zu machen.

Der Code zum iportieren wird dann zu einem Einzeiler, der Code den Du bisher hast wird vollständig überflüssig.
DoCmd.TransferText acImportFixed, NameDerSpezifikation
Mehr braucht es dann nicht.
Der Befehl TransferText hat noch mehr Parameter, die in der Hilfe nachgeschlagen werden können.
Gruß Klaus

ebs17

Nebenbei:
ZitatBei 1 Mio. Datensätzen benötigt mein optimierter Code 48 s, die Variante von markus888 41 s.
Diese Codes erfordern, dass der CSV-Inhalt bereits in der Accesstabelle ist. Dahin muss er erst einmal kommen ... was auch mindestens eine Anweisung und etwas Zeit braucht.

Zitatdas soll auch ein ganz Unwissender per Button erledigen können
Klar, der (generell unwissende) User darf nur ein Knöpfchen drücken). Daher legt der wissende Entwickler einen geeigneten, vollständigen und möglichst fehlerresistenten Code in die Ereignisprozedur des Knöpfchens.
Mit freundlichem Glück Auf!

Eberhard

markusxy

Zitat von: stacc am Juli 01, 2019, 13:43:51
oder kann ich bei meinem Code noch was rausholen?

Ja natürlich, du machst es immer noch viel zu kompliziert.
Das Verwenden der Importfunktion wäre dann auch eine Möglichkeit, die sicher noch schneller ist.

Ich verwende die Importfunktion so gut wie nie, da ich beim Import die Daten auch immer gleich normalisiere. Sprich Texte in Indexwerte umwandle und da ist dann der zeilenweise Import - wenn effizient gemacht - um ein vielfaches schneller.
Andererseits - welche Rolle spielt es ob das 1 Minute oder 10 Sekunden dauert?

ebs17

Zitatder zeilenweise Import ... um ein vielfaches schneller
Das würde ich als hingestellten Grundsatz für recht diskutabel halten. Schleifen statt Massendatenverarbeitung?
Bei einer zu normalisierenden Struktur werden eine Million Datensätze in sich schon recht viele Duplikate mitbringen, von vorhandenen Beständen wollen wir da gar nicht reden. Da muss also vor dem Schreiben also auch geprüft und fallweise entschieden werden, Schlüssel müssen besorgt werden. Das wertweise in Schleifen?

Zitatwenn effizient gemacht
Dieses Bemühen wird man doch an allen Fronten haben ...?

Nebenbei: Import heißt für mich, dass die Daten aus der externen Quelle am Ende verwendungsfertig in den Tabellen des geplanten Datenmodells liegen, umfasst also sämtliche Maßnahmen, die dazu notwendig sind.
Das wäre also etwas mehr, als den nächstbesten Knopf oder die nächstbeste Anweisung zu verwenden, um die externen Daten irgendwie in die Datenbank zu bringen, damit so aber noch wenig anfangen zu können.

Zitatkann ich bei meinem Code noch was rausholen?
Interessante Fragestellung, weil angesprochene Alternativen sichtbar ignoriert werden.
Replace ist eine nicht so schlanke Funktion. Diese mehrfach pro Datensatz verwendet und dann alles millionenfach wiederholt - so etwas würde ich als Problem ansehen, so vom reinen Anschauen her.
Mit freundlichem Glück Auf!

Eberhard

markusxy

Zitat von: ebs17 am Juli 02, 2019, 08:39:38
Zitatder zeilenweise Import ... um ein vielfaches schneller
Schleifen statt Massendatenverarbeitung?

Der Vergleich hinkt. Der Access Import macht auch nichts anderes als Byte für Byte zu durchlaufen.
Der Nachteil beim Schreiben in die DB ist die langsame Schnittstelle - entweder Import Anweisung oder Recordset.
Das was aber am meisten Zeit braucht sind unnötige Schreibprozesse die entstehen, wenn man Texte importiert die später dann durch Indexe ersetzt werden. Da wird dann die Aufbereitung per VBA schneller.
In der Regel importiere ich aber große Mengen in den SQL Server. Da erstelle ich dann Files für Bulk Insert. Da bin ich dann trotz Erstellung eines Textfiles etwa 10mal so schnell, wie wenn ich per Import Funktion in Access importiere.
Allerdings verwende ich da auch kein lahmes VBA für die Aufbereitung.  ;)

ebs17

#12
Wenn Du etwas beschreibst, was Du in Praxis eher nicht ausführst, ist die Behauptung, es wäre um Längen schneller, um so problematischer.
Zitat
unnötige Schreibprozesse die entstehen, wenn man Texte importiert die später dann durch Indexe ersetzt werden
Das ist aber auch eine selbstgewählte Methode und nicht objektive Notwendigkeit.
Unnötiges lässt man doch idealerweise gleich weg.
Sowie: Der Index-Begriff scheint bei Dir eine eigene Bedeutung zu haben, abweichend von der üblichen DB-Verwendung.

Zitatentweder Import Anweisung oder Recordset
Ich kenne auch Anfügeabfragen. SQL ist auch nicht in VBA programmiert.
Mit freundlichem Glück Auf!

Eberhard

markusxy

Zitat von: ebs17 am Juli 02, 2019, 20:44:59
Zitat
unnötige Schreibprozesse die entstehen, wenn man Texte importiert die später dann durch Indexe ersetzt werden
Das ist aber auch eine selbstgewählte Methode und nicht objektive Notwendigkeit.

Da hast du natürlich recht. Meine Massenimporte waren aber leider dadurch gekennzeichnet, das die Zeilen unterschiedlich aufgebaut waren und dadurch war das etwas komplizierter. Daher auch meine Beschäftigung mit möglichen Optimierungen beim Import, was aber bei einer normalen Struktur nicht zutrifft, vor allem wenn alle Indexe bereits bestehen. Es wird also nie eine Methode geben die für alle Fälle ideal ist.

ebs17

#14
Nun, es wundert mich schon, wenn feste über Codes diskutiert wird, ohne sich auch nur mit einem Gedanken mit der Datenquelle (CSV) selber zu beschäftigen.
Mit freundlichem Glück Auf!

Eberhard