Domino 9 und frühere Versionen > ND8: Entwicklung
Ideenfindung für Import
gstueb:
--- Zitat von: MarkusL am 25.09.12 - 13:50:21 ---an mich ist die Bitte herangetragen worden, eine Excel-Tabelle durch eine Notes-DB abzulösen, da hierdurch viele Vorteile gegeben sind.
Im Moment steht der Umsetzung gedanklich nur ein unregelmäßiger Import von Daten aus einer txt-Datei entgegen.
In der Datenbank sind derzeit ca. 4.500 Datensätze.
Bei jedem ca. halbjährlichen Import kommen ca. 300 - 500 Datensätze hinzu.
Bei dem Import müsste anhand von einer eindeutigen Kundennummer und einer weiteren eindeutigen Nummer geprüft werden, ob dieser Datensatz bereits existiert.
--- Ende Zitat ---
Hallo Markus,
für den Test, ob ein Dokument bereits vorhanden ist, könntest Du Dir eine spezielle Ansicht basteln. Diese sollte in der ersten Spalte eine Kombination aus Kundennummer und der anderen eindeutigen Nummer enthalten. Angenommen, die beiden Nummern wären numerisch und maximal 10 Stellen lang, dann könntest Du Dir in der Ansicht einen String daraus machen, so dass aus der KDNR 123456789 und der anderen Nummer 63050000 z.B. der String "01234567890063050000" in der ersten Spalte wird.
Im Agent, welcher die TXT-Datei einließt würde ich wie folgt vorgehen
--- Code: ---Alle Dokumente durchgehen und z.B. das Feld bearbeitung="" setzen.
Lese TextDatei
Bilde SuchString aus Kundennummer und anderer eindeutigen Nummer aus TextDatei
Über view.GetDocumentByKey(Suchstring) prüfen, ob Dokument existiert
Dokument existiert
ja --> Änderungen vornehmen
feld bearbeitung auf "geändert" setzen
Dokument existiert nicht
Dokument anlegen und mit Werten füllen
feld bearbeitung auf "neu" setzen
Ende Lese TextDatei
--- Ende Code ---
Über das (optionale) Feld "bearbeitung" könntest Du dann noch Ansichten basteln, in denen nur alle geänderten / hinzugefügten Dokumente aufgelistet werden.
Ich kann Dir hier noch ein paar Code-Samples anhängen. Zum einen eine Klasse für das Einlesen von CSV-Dateien:
--- Code: ---Class CSVImport
Public EOF As Boolean
Public strDelimiter As String
Sub New(strCSVFile As String, strDelimiter As String)
Me.strDelimiter = strDelimiter
Dim strFeldname As String
Dim vFeldArray As Variant
Dim i As Integer
Me.EOF = False
If Not FileExists(strCSVFile) Then
Me.EOF = True
Exit Sub
End If
fileNum% = FreeFile()
Open strCSVFile For Input As fileNum%
If EOF(fileNum%) Then
Me.EOF = True
Exit Sub
End If
Line Input #fileNum%, strZeile
vFeldArray = Split(strZeile,strDelimiter)
If UBound(vFeldArray) = 0 Then
Me.EOF = True
Exit Sub
End If
For i=LBound(vFeldArray) To UBound(vFeldArray)
strFeldname = VereinfacheUeberschrift$(vFeldArray(i))
strFeldliste(strFeldname) = i
Next
End Sub
Sub ReadNext
Line Input #fileNum%, strZeile
vDatenArray = Split(strZeile, strDelimiter)
If EOF(fileNum%) Then
Me.EOF = True
Close fileNum%
End If
End Sub
' Übergabe von Feldnummer oder Feldname, Rückgabe von Feldinhalt
Function Feldwert(vFeld As Variant) As String
Dim iFeldNummer As Integer
Dim strFeldname As String
Dim strFeldwert As String
iFeldNummer = -1
If DataType(vFeld) = 8 Then ' Feldname wurde übergeben
strFeldname = vFeld
strFeldname = VereinfacheUeberschrift$(strFeldname)
If IsElement(strFeldliste(strFeldname)) Then
iFeldNummer = strFeldliste(strFeldname)
End If
Else
iFeldNummer = vFeld
End If
If iFeldNummer >= LBound(vDatenArray) And iFeldNummer <= UBound(vDatenArray) Then
strFeldwert = vDatenArray(iFeldNummer)
strFeldwert = VereinfacheDaten$(strFeldwert )
Else
strFeldwert = ""
End If
Feldwert = strFeldwert
End Function
Public Function FileExists(strFile As String) As Boolean
FileExists = True
Dim dummy As Long
Err=0
On Error Resume Next
dummy = FileLen(strFile)
If Err <> 0 Then
FileExists = False
End If
On Error GoTo 0
End Function
' Entferne Anführungszeichen, außerdem alles in groß
Private Function VereinfacheUeberschrift$(Ueberschrift$)
Dim src$(0)
Dim fnd$(0)
Dim rep$(0)
Dim ret As Variant
src$(0) = Ueberschrift$
fnd$(0) = {"}
rep$(0) = ""
ret = Replace(src$,fnd$,rep$)
VereinfacheUeberschrift$ = Trim(UCase(ret(0)))
End Function
' Entferne Anführungszeichen und Zeilenumbrüche
Private Function VereinfacheDaten$(Daten$)
Dim src$(0)
Dim fnd$(1)
Dim rep$(1)
Dim ret As Variant
src$(0) = Daten$
fnd$(0) = Chr$(10)+Chr$(13)
fnd$(1) = {"}
rep$(0) = " "
rep$(1) = ""
ret = Replace(src$,fnd$,rep$)
VereinfacheDaten$ = ret(0)
End Function
End Class
--- Ende Code ---
und dann noch ein Code-Gerüst für das eben beschriebene:
--- Code: ---Sub Initialize
Dim obCSV As New CSVImport(strFilename, Chr$(9) )
Do Until obCSV.EOF
obCSV.ReadNext
strSuche = Right$("0000000000" + obCSV.Feldwert("Kundennummer"),10)
GoSub BearbeiteDoc
Loop
Exit Sub
' Pro gelesener CSV-Zeile
BearbeiteDoc:
Set view = session.Currentdatabase.GetView( "AnsichtFuerSuche" )
Set doc = view.GetDocumentByKey ( strSuche, True )
If doc Is Nothing Then ' Dokument existiert nicht
Set doc = New NotesDocument ( session.CurrentDatabase )
doc.Form = "Maske"
doc.Feld1 = obCSV.Feldwert("Feld1")
doc.Feld2 = obCSV.Feldwert("Feld2")
else
doc.Feld1 = obCSV.Feldwert("Feld1")
doc.Feld2 = obCSV.Feldwert("Feld2")
End If
Call doc.Save(True,False)
Return
End Sub
--- Ende Code ---
joan:
Hallo Markus,
--- Zitat ---In der Datenbank sind derzeit ca. 4.500 Datensätze.
Bei jedem ca. halbjährlichen Import kommen ca. 300 - 500 Datensätze hinzu.
Bei dem Import müsste anhand von einer eindeutigen Kundennummer und einer weiteren eindeutigen Nummer geprüft werden, ob dieser Datensatz bereits existiert.
Wenn ja, soll der bestehende mit neuen Infos ergänzt werden,
wenn nein, soll der Datensatz neu angelegt werden.
--- Ende Zitat ---
Ich mache das performanceoptimiert so:
1. Der Import liest per OLE die Excel Zeilen ein. Vorher passiert noch (4.)
2. Über die gelesene Excel Zeile wird ein Hash-Wert berechnet, der zusätzlich zu den Daten im angelegten Dokument gespeichert wird (geht am einfachsten und schnellsten mit Evaluate("@Password(<geleseneZeile>)").
3. In der Datenbank gibt es eine versteckte Ansicht, die zwei Spalten hat: Erste Spalte ist eine eindeutige Kennung des Datensatzes (sortiert), also die Kundennummer + weitere Nummern. Die zweite Spalte ist der Hash Wert des Dokuments.
4. Als erstes im Import Agent läuft ein viewNavigator über diese Ansicht, holt sich die Werte per viewEntry.Columnvalues() und merkt sich alle Kombinationen in einer Liste: Hash(<eindeutiger Schlüssel>) = Hashwert. Das geht durch den viewNavigator rasend schnell, weil keine Dokumente gelesen werden, sondern nur auf den view Index zugegriffen wird. Dem kann man sicherheitshalber vorher noch ein view.Refresh() geben.
5. Nach jeder gelesenen Excel Zeile berechnet der Import Agent erst einmal den Hash Wert dieser Zeile und prüft, ob er in der Hash Liste aus (4) enthalten ist. Wenn ja, ist der Datensatz vorhanden und wurde nicht geändert, also nächster Datensatz. Wenn der Wert nicht gefunden wird, ist der Datensatz neu oder wurde geändert, also Datensatz per view.GetDocumentByKey(.., True) holen und aktualisieren bzw. neu anlegen. Nicht vergessen, den Hash Wert beim Update neu zu berechnen.
Das ganze ist extrem schnell, weil die Dokumente nur dann gelesen werden müssen, wenn an diesem Dokument auch tatsächlich Änderungen gemacht wurden. Wenn nur wenige Änderungen nötig sind, ist der Agent selbst bei großen Datenbanken schneller fertig, als man die Konsole aufrufen kann, um ihn zu kontrollieren... ;) Die einzig bremsende Komponente bei dem Verfahren ist nur noch der OLE Zugriff. Wenn man den durch Textdateien ersetzt, wird das ganze noch einmal ein gutes Stück schneller. Das würde ich vor allem dann machen, wenn größere Änderungen zu erwarten sind.
Peter Klett:
Ganz interessanter Ansatz. Es darf nur nicht bei zwei unterschiedlichen Datensätzen der gleiche Hashwert herauskommen, was man bei dokumentenbasierten Anwendungen, für die Notes ideal ist, nicht zwingend ausschließen kann. In diesem Fall mit einer eindeutiger Nummer, also eher einem relationalen Ansatz, stellt das natürlich kein Problem dar.
joan:
Hallo Peter,
Der Ansatz stammt nicht von mir - die Idee habe ich von Andrew Pollack, der das auf dem Entwickler-Camp schon des öfteren beschrieben hat. Die meisten kennen vermutlich seine Story, wie er damit einen Datenbankserver in die Knie gezwungen hat... ;)
--- Zitat ---Es darf nur nicht bei zwei unterschiedlichen Datensätzen der gleiche Hashwert
--- Ende Zitat ---
Stimmt, die Wahrscheinlichkeit, dass das passiert, ist vorhanden, aber sie ist extrem gering. Die Algorithmen sind ja gerade darauf ausgelegt, daß das nach Möglichkeit nicht passiert. Und wer @Password mißtraut, kann natürlich auch einen eigenen Algorithmus implementieren, das ist dann halt langsamer. Ich habe das Verfahren seit Jahren in diversen Applikationen mit Millionen von Datensätzen und täglichen Updates im Einsatz und bisher kam es noch nie vor, dass ein Datensatz wegen zufälliger Gleichheit nicht aktualisiert wurde. So gesehen würde ich das Verfahren aus eigener Erfahrung als sicher bezeichnen.
Peter Klett:
Nun, ich meinte, dass der Hashwert identisch ist, wenn auch die Daten identisch sind (nicht, dass er es bei ungleichen Daten sein kann).
Z.B. hast Du als Daten die Artikelnummer, die Kundennummer, die Anzahl und das Datum des Einkaufs
Jetzt kauft der Kunde am gleichen Tag den gleichen Artikel zweimal. Ist im Datum keine Uhrzeit enthalten, ist der Datensatz IDENTISCH, aber trotzdem zweimal da, und das auch berechtigt. Wenn Du dann beim Import mit der Routine Doppelimporte verhinderst, fehlt Dir ein Umsatz.
Navigation
[0] Themen-Index
[#] Nächste Seite
[*] Vorherige Sete
Zur normalen Ansicht wechseln