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
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.
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.
Hallo,
eine ACCDB musst Du packen (Zip), das geht nicht anders. ACCDB ist zum Hochladen nicht zugelassen.
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.
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)
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 SubZitat 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
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, NameDerSpezifikationMehr braucht es dann nicht.
Der Befehl TransferText hat noch mehr Parameter, die in der Hilfe nachgeschlagen werden können.
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.
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?
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.
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. ;)
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.
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.
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.
Hintergrund:
Zu Beginn wurde die CSV des Scanners (MDE) manuell mit der Importfunktion eingelesen und direkt gesplittet. Die ersten drei Werte Lagerort, Material und Menge werden derzeit weiterverarbeitet. Es sind aber immer ein paar Datensätze fehlerhaft, die dann Schwierigkeiten machen. Fehler sind: Lagerort zweimal gescannt statt Material oder umgekehrt. Barcode falsch gescannt / nicht korrekt erkannt, Barcodeaufkleber des Lieferanten erwischt...
Kenntnisse wie eine Kontrolle während des Scannens erfolgen kann sind nicht vorhanden.
Also wurde die CSV-Zeile (mit 5 Werten) als komplette Zeile eingelesen und dann grob auf Fehler geprüft. Sind genau 4 Komma je Zeile vorhanden, gibt es nur ein M, ist die Zeichenzahl <= 47. Die Zeilennummer mit Fehler wird in einem Bericht ausgegeben und dann wird ggf. im Lager nachgesehen wo der Fehler ist. Grob sind 0,5% der Datensätze fehlerhaft.
Die Datensätze werden dann in einer Tabelle manuell korrigiert. Dann werden sie gesplittet.
Als Zwischenschritt wird noch geprüft ob die gescannte / gesplittete Materialnummer wirklich vergeben ist. Der Rest wird geglaubt. Dann werden die Mengen in den jeweiligen Bestand übernommen. Mehr gibt es bisher nicht. Die Tabelle mit korrigierten Werten soll dann separat gespeichert werden, damit jeder z. B. mal nachsehen kann (mittels Suchfunktion), wo ein bestimmter Artikel gelagert wird.
Die 1 Mio. Datensätze sind ja nur hypothetisch, tatsächlich ca. 1500. Somit ist die Methode bezüglich Geschwindigkeit nicht wirklich wichtig. Aber man kann ja mal größer denken und einfach etwas lernen.
VG
Stefan
Hallo Stefan,
ZitatFehler sind: Lagerort zweimal gescannt statt Material oder umgekehrt. Barcode falsch gescannt / nicht korrekt erkannt, Barcodeaufkleber des Lieferanten erwischt...
Das muss aber doch die Software auf dem Scanner bereits abfangen.
Da würde ich mal ansetzen.
gruss ekkehard