Hier (http://atnotes.de/index.php?topic=15201.0) gab es mal eine Abhandlung zu dem Thema. Ich habe gerade eben nochmal in einer 80.000 Dokumente View den Vergleich zwischen NotesViewNavigator und NotesViewEntryCollection laufen lassen, und kann die Ergebnisse für Notes 9.0.1 nicht bestätigen. Bei mir hat der NotesViewNavigator mit 9 gegenüber 14 Sekunden klar gewonnen...
Also dieser Code:
Dim varTest as Variant
Dim viwTest as NotesView
Dim vec as NotesViewEntryCollection
Dim viwNav as NotesViewNavigator
Dim ve as NotesViewEntry
Dim dblStart as Double
Dim varX as Variant
dblStart = Now
Set viwTest = g_dbFocus.GetView( "($All)" )
Set viwNav = viwTest.CreateViewNav
Set ve= viwNav.GetFirst()
While not ve is Nothing
varX = ve.ColumnValues(0)
Set ve = viwNav.GetNext(ve)
Wend
Messagebox Now - dblStart
gewinnt mit nur 9 sec. Laufzeit gegen Dim varTest as Variant
Dim viwTest as NotesView
Dim vec as NotesViewEntryCollection
Dim viwNav as NotesViewNavigator
Dim ve as NotesViewEntry
Dim dblStart as Double
Dim varX as Variant
dblStart = Now
Set viwTest = g_dbFocus.GetView( "($All)" )
Set vec = viwTest.AllEntries
Set ve=vec.GetFirstEntry()
While not ve is Nothing
varX = ve.ColumnValues(0)
Set ve = vec.GetNextEntry(ve)
Wend
Messagebox Now - dblStart
diesen Code mit 14 sek. Laufzeit. (auf zwei verschiedenen Datenbanken getestet, die beide zwischen 80.000 und 90.000 Dokumenten haben)
in Excel kann durch das setzen der ScreenUpdating-Eigenschaft die Performance erhöht werden:
excel.ScreenUpdating = false
'-- Excel Werte setzen
excel.ScreenUpdating = True
Hallo Zusammen,
hier nun die angekündigt Rückmeldung:
Die große Bremse beim Export ist das Auslesen der 159 Spalten mit der Methode "viewentry.ColumnValues(iCols)". Diese benötigt mit Abstand die meiste Zeit. Zudem muss dann noch der Inhalt auf Chr$(10) und Chr$(13) sowie auf das Delimiterzeichen ";" gescannt werden, bevor ich den Datensatz in die CSV Datei schreiben kann. Schreibe ich nur die erste Spalte in die CSV Datei, benötigt die Funktion nur noch 8 Sekunden!
Grüße
Bernd
Äh du machst aber nicht wirklich viewentry.ColumnValues(iCols) in deinem Code, oder?. viewentry.ColumnValues gibt dir doch ein Array zurück, dass du natürlich zwischenspeichern solltest und nicht immer wieder über das API holen solltest. Wenn du Glück hast, cached der API Aufruf auch, ich würde mich aber nicht darauf verlassen.
Wenn du maximale Performance willst, dann solltest du die View auf Autoupdate false setzen und dann beim NotesViewNavigator den BufferMaxEntries auf 200 setzen. Das sollte das Auslesen der View sehr stark beschleunigen. Noch weitere Performance könnte es bringen, wenn du von Lotusscript auf einen Javaagenten umsteigst.
Der Code sollte dann ungefähr so aussehen.
Dim varTest as Variant
Dim viwTest as NotesView
Dim vec as NotesViewEntryCollection
Dim viwNav as NotesViewNavigator
Dim ve as NotesViewEntry
Dim dblStart as Double
Dim varX as Variant
dblStart = Now
Set viwTest = g_dbFocus.GetView( "($All)" )
viwTest.autoupdate=false
Set viwNav = viwTest.CreateViewNav
viwNav.buffermaxentries=200
Set ve= viwNav.GetFirst()
While not ve is Nothing
varX = ve.ColumnValues()
Set ve = viwNav.GetNext(ve)
Wend
Messagebox Now - dblStart
Grüße
Ralf
Eine Idee wäre es das ganze nicht einer Variable zuweisen, sondern direkt in der For all Schleife zu verwenden.
Sinngemäß, da ich jetzt keinen Designer zur Hand habe.
forall werte in ve.columnvalues
if isArray(werte) then
rem Mache Verarbeitung multi value
else
rem Mache Verarbeitung Single Value
end if
end forall
Ich hoffe, dass das funktioniert, aber ich kann es derzeit nicht testen, da ich morgen unterwegs bin.
Hallo Zusammen,
nachdem ich gesehen habe, dass hier wohl reges Interesse an diesem Thema ist, anbei meinen vereinfachten Quellcode.
Vielleicht kann den ja mal jemand brauchen. ;)
Zielsetzung:
- Möglichst performanten Datenexport. Datenbasis mit sehr großen Datenmengen. (100.000 Dokumente und mehr, Ansicht mit 160 Spalten)
Bedingungen:
- Die Exportansicht darf keine Multivalue Einträge beinhalten. Diese müssen zuvor in der Ansicht mit dem @Implode Befehl in einen String umgewandelt werden.
- Der Export wird als CSV Datei geschrieben und kann dann in EXCEL importiert werden.
Verbesserungsvorschläge zur Performanceoptimierung sind erwünscht!
Grüße
Bernd
Sub Initialize
On Error GoTo errhandler
Dim ses As New NotesSession
Dim db As NotesDatabase
Dim view As NotesView
Dim viewColumn As NotesViewColumn
Dim viewentry As NotesViewEntry
Dim viewNav As NotesViewNavigator
Dim vColValues As Variant
Dim lDocQuantity As Long
Dim iColQuantity As Integer
Dim iK As Integer
Dim vHeadline As Variant
Dim sHeadline As String
Dim sPath As String
Dim sFilename As String
Dim iFilenum As Integer
Dim sLine As String
Dim lLauf As long
Dim zwerg As String
Set db = ses.currentDatabase
Set view = db.Getview("Excel-Export")
If view Is nothing Then
MsgBox "EXCEL-Export Ansicht ist nicht vorhanden!", 64, "Hinweis"
Exit sub
End If
view.Autoupdate = False
iColQuantity=view.ColumnCount
Set viewNav = view.Createviewnav()
lDocQuantity = viewNav.Count
viewNav.BufferMaxEntries = 400
viewNav.EntryOptions = Vn_entryopt_nocountdata
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'Spaltenüberschriften ermitteln
vHeadline = view.columns()
For iK=0 To iColQuantity-1
If iK = 0 Then
sHeadline = vHeadline(iK).title
Else
sHeadline = sHeadline +";" + vHeadline(iK).title
End If
Next
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'Export Dateiname
sFilename = "C:\temp\All-EXCEL-export.csv"
Print sFilename
'Falls die Datein vorhanden ist, wird diese gelöscht.
On Error Resume Next
Kill sFilename
On Error GoTo errhandler
iFilenum = FreeFile
Open sFilename For Output As iFilenum
Print #iFilenum, sHeadline
Set viewentry = viewNav.Getfirst()
Do While Not (viewentry Is Nothing)
lLauf = lLauf+ 1
If llauf Mod 20 = 0 Then
Print "Export Datensatz... " & lLauf & " / " & lDocQuantity
End if
vColValues=viewentry.ColumnValues()
For iK = 0 To iColQuantity-1
zwerg = vColValues(iK)
zwerg = Replace(zwerg, Chr$(10), " ")
zwerg = Replace(zwerg, Chr$(13), " ")
If iK = 0 Then
sLine = zwerg
Else
sLine = sLine +";" + zwerg
End If
Next
'Daten schreiben...
Print #iFilenum, sLine
Set viewentry = viewNav.GetNext(viewentry)
Loop
Close iFilenum
MsgBox "Ende", 64, "Note"
Exit Sub
errhandler:
MsgBox "Error in Agent: MS-EXCEL export Error: " & Error & " (" & Err & ") in Zeile: " & Erl, 64, "Hinweis"
close
Exit Sub
End Sub
Hallo Forum,
hier noch eine kleine Quellcodeänderung die es nochmals deutlich performanter machte:
Insgesamt wurden aufgrund der diversen Optimierungen die Exportzeit in der TestDB von 1 Minute 40 Sek. auf 5 Sekunden reduziert. :)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dim s(2) As String, r(2) As String
s(0) = Chr(10)
s(1) = Chr(13)
s(2) = ";"
r(0) = " "
r(1) = " "
r(2) = ","
'Export Dateiname
sFilename = "C:\temp\All-EXCEL-export.csv"
Print sFilename
'Falls die Datein vorhanden ist, wird diese gelöscht.
On Error Resume Next
Kill sFilename
On Error GoTo errhandler
iFilenum = FreeFile
Open sFilename For Output As iFilenum
Print #iFilenum, sHeadline
Set viewentry = viewNav.Getfirst()
Do While Not (viewentry Is Nothing)
lLauf = lLauf+ 1
If llauf Mod 20 = 0 Then
Print "Export Datensatz... " & lLauf & " / " & lDocQuantity
End if
vColValues=viewentry.ColumnValues()
For iK = 0 To iColQuantity-1
If lDataSet = 0 Then
sb.append(Replace(cstr(vColValues(lDataSet)), s, r))
Else
sb.append(";" + Replace(CStr(vColValues(lDataSet)), s, r))
End If
Next
'Daten schreiben...
Print #iFilenum, sLine
Set viewentry = viewNav.GetNext(viewentry)
Loop
Close iFilenum
Hi Tode,
das war vielleicht etwas missverständlich. Ich wollte nur den Teil mit dem "Replace" posten. Als Ergänzung zum bestehenden Code.
Hier aber für alle nochmals der gesamte Code mit Einbindung der StringBuffer Klasse, die Tode weiter oben erwähnt hatte.
Sub Initialize
On Error GoTo errhandler
Dim ses As New NotesSession
Dim db As NotesDatabase
Dim view As NotesView
Dim viewColumn As NotesViewColumn
Dim viewentry As NotesViewEntry
Dim viewNav As NotesViewNavigator
Dim vColValues As Variant
Dim lDocQuantity As Long
Dim iColQuantity As Integer
Dim iK As Integer
Dim vHeadline As Variant
Dim sHeadline As String
Dim sPath As String
Dim sFilename As String
Dim iFilenum As Integer
Dim sLine As String
Dim lLauf As long
Dim zwerg As String
Set db = ses.currentDatabase
Set view = db.Getview("Excel-Export")
If view Is nothing Then
MsgBox "EXCEL-Export Ansicht ist nicht vorhanden!", 64, "Hinweis"
Exit sub
End If
view.Autoupdate = False
iColQuantity=view.ColumnCount
Set viewNav = view.Createviewnav()
lDocQuantity = viewNav.Count
viewNav.BufferMaxEntries = 400
viewNav.EntryOptions = Vn_entryopt_nocountdata
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'Spaltenüberschriften ermitteln
vHeadline = view.columns()
For iK=0 To iColQuantity-1
If iK = 0 Then
sHeadline = vHeadline(iK).title
Else
sHeadline = sHeadline +";" + vHeadline(iK).title
End If
Next
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'Export Dateiname
sFilename = "C:\temp\All-EXCEL-export.csv"
Print sFilename
'Falls die Datein vorhanden ist, wird diese gelöscht.
On Error Resume Next
Kill sFilename
On Error GoTo errhandler
iFilenum = FreeFile
Open sFilename For Output As iFilenum
Print #iFilenum, sHeadline
Dim sb As New StringBuffer(30)
Dim s(2) As String, r(2) As String
s(0) = Chr(10)
s(1) = Chr(13)
s(2) = ";"
r(0) = " "
r(1) = " "
r(2) = ","
Set viewentry = viewNav.Getfirst()
Do While Not (viewentry Is Nothing)
lLauf = lLauf+ 1
If llauf Mod 20 = 0 Then
Print "Export Datensatz... " & lLauf & " / " & lDocQuantity
End if
vColValues=viewentry.ColumnValues()
For iK = 0 To iColQuantity-1
If iK = 0 Then
sb.append(Replace(CStr(vColValues(iK)), s, r))
Else
sb.append(";" + Replace(CStr(vColValues(iK)), s, r))
End If
Next
'Daten schreiben...
Print #iFilenum, sb.toString
Call sb.Erase
Set viewentry = viewNav.GetNext(viewentry)
Loop
Close iFilenum
MsgBox "Ende", 64, "Note"
Exit Sub
errhandler:
MsgBox "Error in Agent: MS-EXCEL export Error: " & Error & " (" & Err & ") in Zeile: " & Erl, 64, "Hinweis"
close
Exit Sub
End Sub
Jetzt hast Du aber immer noch ein "+" drin, Du kriegst also noch ein "klitzekleines" bisschen mehr raus, wenn Du die Zeile
sb.append(";" + Replace(CStr(vColValues(iK)), s, r))
ersetzt durch
sb.append(";")
sb.append(Replace(CStr(vColValues(iK)), s, r))
Interessantes Thema und interessanter Thread.
Frage: was spricht gegen Implode?
Statt
Do While Not (viewentry Is Nothing)
lLauf = lLauf+ 1
If llauf Mod 1000 = 0 Then
Print "Export Datensatz... " & lLauf
End If
vColValues=viewentry.ColumnValues()
For iK = 0 To iColQuantity-1
If iK = 0 Then
sb.append(Replace(CStr(vColValues(iK)), s, r))
Else
sb.append(";" + Replace(CStr(vColValues(iK)), s, r))
End If
Next
'Daten schreiben...
Print #iFilenum, sb.toString
Call sb.Erase
Set viewentry = viewNav.GetNext(viewentry)
Loop
einfach
Do While Not (viewentry Is Nothing)
lLauf = lLauf+ 1
If llauf Mod 1000 = 0 Then
Print "Export Datensatz... " & lLauf
End If
vColValues=viewentry.ColumnValues()
'Daten schreiben...
Print #iFilenum, Implode(vColValues,";")
Set viewentry = viewNav.GetNext(viewentry)
Loop
macht in meiner Test-DB (exportierte Excel-Datei 49.152 Zeilen, 90,7MB) einen Unterschied von 33s --> 12s Gesamt-Laufzeit aus. Sprich: der Export ist in etwas mehr als einem Drittel der Zeit fertig.
;D
Vielleicht sind Deine Daten ja so sauber in der View, daß Du gar kein großartiges Brimborium wie StringBuffer etc. brauchst? Versuch's doch mal...
Gruß,
Th.
Update: Habe in meiner Export-Ansicht jede Menge weitere Spalten eingefügt. 90s --> 38s. Wahrscheinlich geht's doch eher nur in Richtung 50%+ Zeiteinsparung. :-\