[VB] performance readline progje

Pagina: 1
Acties:

  • huistra
  • Registratie: April 2001
  • Laatst online: 08-05-2024
Ik heb een textfile waarin op 1 regel een hele (~620.000 posities) zijn weggeschreven. Deze textfile wil ik inlezen in een acces database waarin steeds delen van de regel worden weggeschreven als record, net zo lang tot de hele regele verwerkt is. Ik heb hiervoor de volgende code geklust:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Option Compare Database

Private Sub Draai_query_Click()
       
    MsgBox ("Het ophalen van de data start, dit kan geruime tijd duren")
    
    Dim RCS As DAO.Recordset
    Dim DBS As DAO.Database
    Dim Teller As String
    Dim Tekst As String
    Dim Rekening As String
    Dim Controlegetal1 As String
    Dim Controlegetal2 As String
    Dim Naam As String
    Dim Zakenpartner As String
    Dim fso, MyFile
    
    Set DBS = CurrentDb()
    Set RCS = DBS.OpenRecordset("Resultaat")
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    
    
    Teller = 2049
    'MsgBox (Tekst)
    
    Do While Not Teller > 620545
        
        Set MyFile = fso.OpenTextFile("C:\ANC.txt")
        Tekst = MyFile.Readline
        Rekening = Nz(Mid(Tekst, Teller, 7))
        Controlegetal1 = Nz(Mid(Tekst, Teller + 7, 1))
        Controlegetal2 = Nz(Mid(Tekst, Teller + 8, 1))
        Naam = Nz(Mid(Tekst, Teller + 16, 32))
        Zakenpartner = Nz(Mid(Tekst, Teller + 48, 16))
        
        RCS.AddNew
        RCS!Rekening = Rekening
        RCS!Controlegetal1 = Controlegetal1
        RCS!Controlegetal2 = Controlegetal2
        RCS!Naam = Naam
        RCS!Zakenpartner = Zakenpartner
        RCS.Update
        
        MyFile.Close
        Teller = Teller + 64
                
    Loop
    
    RCS.Close
        
    MsgBox ("Ophalen gegevens voltooid")

End Sub

Het stukje code doet precies wat het moet doen, dus functioneel gaat alles goed. Maarrrr de performance is nogal beroerd. Ik heb hem een nachtje (~8 uur) laten aanstaan en toen had 'ie slechts 7000 records (van 64 posities) weggeschreven. ;(

Ik vermoed dat het komt doordat 'ie een dergelijk lange string moet inlezen, maar weet niet zo goed hoe ik de performance kan verbeteren. Ik heb al geprobeer om niet een MyFile.Close te doen om zo de regel ingelezen te houden, maar dan krijg ik een error in het readline statement.
Tips? :)

  • j_du_pee
  • Registratie: Maart 2000
  • Laatst online: 23-09-2024

j_du_pee

du pain, du vin, du pee

Dat openen en closen in de loop kan sowieso niet goed zijn nee :)
Ik denk dat het handig is om de file niet in 1 keer in te lezen, maar er een stream van te maken waar je steeds x chars uit leest ;)
ziets: http://www.devguru.com/technologies/vbscript/14124.asp

edit:
zo zou ik het dus ongeveer doen (wel met fso, ken die build in objecten niet zo die verderop worden genoemd. Denk dat dit evengoed flinke winst oplevert ;) )
code:
1
2
3
4
5
6
set filesys = CreateObject("Scripting.FileSystemObject")
set readfile = filesys.OpenTextFile("groottekstbestand.txt", 1, false)
do while readfile.AtEndOfStream <> true
    <insertfunctie> readfile.Read(100)
loop 
readfile.close

[ Voor 45% gewijzigd door j_du_pee op 09-02-2006 12:31 ]

kaart != map && bottel != fles
Wacht op antwoord


  • Shuisman
  • Registratie: Maart 2004
  • Laatst online: 04-04 19:20
j_du_pee schreef op donderdag 09 februari 2006 @ 12:14:
Dat openen en closen in de loop kan sowieso niet goed zijn nee :)
Ik denk dat het handig is om de file niet in 1 keer in te lezen, maar er een stream van te maken waar je steeds x chars uit leest ;)
ziets: http://www.devguru.com/technologies/vbscript/14124.asp
Kan dat openen en closen niet buiten de loop ?
En is het gebruik van een for...next loop niet wat efficienter ?
code:
1
2
3
for teller = 4096 to 620545 step 64 
 (aldieregeltjes)
next teller


En wat doet Nz() eigenlijk ?

Een andere manier van inlezen is: (weet niet als dit beter is, ik gebruik altijd die jij ook gebruikt)
code:
1
2
3
Open "file.dat" For Input As 1
 Input #1,  variabele
Close #1


Je zou (om het wat overzichtelijker te maken) dit :
code:
1
2
3
4
5
6
7
        RCS.AddNew
        RCS.Rekening = Rekening
        RCS.Controlegetal1 = Controlegetal1
        RCS.Controlegetal2 = Controlegetal2
        RCS.Naam = Naam
        RCS.Zakenpartner = Zakenpartner
        RCS.Update

kunnen vervangen door:
code:
1
2
3
4
5
6
7
8
9
With RCS
        .AddNew
        .Rekening = Rekening
        .Controlegetal1 = Controlegetal1
        .Controlegetal2 = Controlegetal2
        .Naam = Naam
        .Zakenpartner = Zakenpartner
        .Update
End with


Nog beter zou zijn:
code:
1
2
3
4
5
6
7
8
9
        Set MyFile = fso.OpenTextFile("C:\ANC.txt")
        Tekst = MyFile.Readline
        RCS.AddNew
        RCS.Rekening = Nz(Mid(Tekst, Teller, 7))
        RCS.Controlegetal1  = Nz(Mid(Tekst, Teller + 7, 1))
        RCS.Controlegetal2 = Nz(Mid(Tekst, Teller + 8, 1))
        RCS.Naam = Nz(Mid(Tekst, Teller + 16, 32))
        RCS.Zakenpartner = Nz(Mid(Tekst, Teller + 48, 16))
        RCS.Update

[ Voor 62% gewijzigd door Shuisman op 09-02-2006 12:52 ]


  • Lorn
  • Registratie: Maart 2000
  • Laatst online: 13-01-2025

Lorn

I have a bad feeling...

Open de file niet met het FSO maar gebruik de routines voor bestanden die VB6 heeft (FreeFile, Open, Seek, Get, etc.). Als je het bestand dan als Random opent kun je steeds een deel inlezen uit het bestand. Aangezien je record altijd 64 karakters is gaat dit super makkelijk. Gebruik eventueel EOF om te testen of het einde van het bestand is bereikt.

Wat je verder nog kunt doen is ipv het recordset object te gebruiken gebruik te maken van het command object van ADO. Als je dan een SQL-query met parameters maakt dan en de Prepared property op true zet boek je volgens mij ook aardig wat winst. Niet vergeten om aan de Execute methode de optie adExecuteNoRecords mee te geven om de hoeveelheid data die tussen je applicatie en de database wordt verstuurd te minimaliseren.

[ Voor 14% gewijzigd door Lorn op 09-02-2006 12:30 ]


  • Lorn
  • Registratie: Maart 2000
  • Laatst online: 13-01-2025

Lorn

I have a bad feeling...

Dat gaat qua performance niks uitmaken. Je blijft de hele regel in een keer inlezen. Alle stingbewerkingen worden dus op die hele regel gedaan. Dit kost iedere keer weer ontzettend veel tijd.

Verwijderd

de opentextfile en close moet buiten de do while loop, ik kan me niet voorstellen dat de stringbewerkingen zoveel tijd kosten.je kan eventueel ook nog het resultaat naar een andere tekstfile wegschrijven en op het einde een select into doen.

  • Lorn
  • Registratie: Maart 2000
  • Laatst online: 13-01-2025

Lorn

I have a bad feeling...

Verwijderd schreef op donderdag 09 februari 2006 @ 14:04:
de opentextfile en close moet buiten de do while loop, ik kan me niet voorstellen dat de stringbewerkingen zoveel tijd kosten.je kan eventueel ook nog het resultaat naar een andere tekstfile wegschrijven en op het einde een select into doen.
Misschien niet maar ik kan me eigenlijk ook niet voorstellen dat het in het openen en sluiten van het bestand zit. Het gaat om iets meer dan 620K karakters, ruim een halve megabyte. Dat moet toch niet al te lang duren om te doen. Volgens mij laat Windows de data in zijn geheugen staan, dan hoeft het bestand ook niet steeds van disk gelezen te worden.

Eigenlijk is het wel interessant om te weten wat voor specificaties de PC heeft waarop de applicatie draait. 620K karakters, 64 karakters per record komt uit op bijna 9700 records. In 8 uur tijd waren er 7000 records gedaan, dat is maar 875 per uur. Dat is 1 record per 4 seconden! Dat is gewoon echt bagger traag.

  • huistra
  • Registratie: April 2001
  • Laatst online: 08-05-2024
j_du_pee schreef op donderdag 09 februari 2006 @ 12:14:
Dat openen en closen in de loop kan sowieso niet goed zijn nee :)
Ik denk dat het handig is om de file niet in 1 keer in te lezen, maar er een stream van te maken waar je steeds x chars uit leest ;)
ziets: http://www.devguru.com/technologies/vbscript/14124.asp

edit:
zo zou ik het dus ongeveer doen (wel met fso, ken die build in objecten niet zo die verderop worden genoemd. Denk dat dit evengoed flinke winst oplevert ;) )
code:
1
zinvolle code
Dank voor je hulp; hij loopt nu in een minuut of twee volledig!! :)
Ook anderen dank voor het meedenken...

  • Lorn
  • Registratie: Maart 2000
  • Laatst online: 13-01-2025

Lorn

I have a bad feeling...

LOL, had net ff een stukje code geschreven dat volgens mij lekker snel is :) Het zal hier en daar nog wel wat debugging nodig hebben (het opdelen van de ingelezen buffer is niet foutloos), was eigenlijk wel een beetje benieuwd hoe snel het zou zijn met je tekstbestand. Mocht je de code een keertje testen dan ben ik wel geïnteresseerd in het resultaat. Onderstaande code is trouwens zonder enige error checking, dat is even bewust gedaan wegens de tijd.

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Private Sub Draai_query_Click()
    Dim objDatabase  As ADODB.Connection
    Dim objInsertQry As ADODB.Command
    Dim strBuffer    As String * 64
    Dim intFile      As Integer
    
    'Open een ADO database connectie
    Set objDatabase = New ADODB.Connection
    objDatabase.Provider = "Microsoft.JET.OLEDB.4.0"
    objDatabase.Properties.Item("Data Source").Value = MyAccessMdb
    objDatabase.Open
    
    'Maak het Command object aan met de parameters voor de SQL query
    Set objInsertQry = New ADODB.Command
    Call objInsertQry.Parameters.Append(objInsertQry.CreateParameter("Rekening", adVarChar, adParamInput, 7))
    Call objInsertQry.Parameters.Append(objInsertQry.CreateParameter("Controle1", adVarChar, adParamInput, 1))
    Call objInsertQry.Parameters.Append(objInsertQry.CreateParameter("Controle2", adVarChar, adParamInput, 1))
    Call objInsertQry.Parameters.Append(objInsertQry.CreateParameter("Naam", adVarChar, adParamInput, 32))
    Call objInsertQry.Parameters.Append(objInsertQry.CreateParameter("Zakenpartner", adVarChar, adParamInput, 16))
    objInsertQry.CommandText = "INSERT INTO Resultaat(Rekening, Controlegetal1, Controlegetal2, Naam, Zakenpartner) VALUES (?, ?, ?, ?, ?)"
    Set objInsertQry.ActiveConnection = objDatabase
    objInsertQry.Prepared = True
    
    'Open het bestand en verplaats de filepointer naar het begin van de data
    intFile = FreeFile
    Open MyTextFile For Random As #intFile Len = 64
    Seek #intFile, 2049
    
    'Lees alle records 1 voor 1 in uit de tekstfile
    While Not EOF(intFile)
      'Lees een record in uit de file
      Get #intFile, , strBuffer
      'Plaats de gegevens van de records in de parameters
      objInsertQry.Parameters.Item("Rekening").Value = Nz(Left$(strBuffer, 7))
      objInsertQry.Parameters.Item("Controle1").Value = Nz(Mid$(strBuffer, 8, 1))
      objInsertQry.Parameters.Item("Controle2").Value = Nz(Mid$(strBuffer, 9, 1))
      objInsertQry.Parameters.Item("Naam").Value = Nz(Mid$(strBuffer, 17, 32))
      objInsertQry.Parameters.Item("Zakenpartner").Value = Nz(Mid$(strBuffer, 48, 16))
      'Voer het command uit
      Call objInsertQry.Execute(, , adExecuteNoRecords)
    Wend
    
    'Sluit de file
    Close #intFile
    'Sluit het command object
    Set objInsertQry = Nothing
    'Sluit de connectie
    objDatabase.Close
    Set objDatabase = Nothing
End Sub

[ Voor 12% gewijzigd door Lorn op 09-02-2006 23:17 ]

Pagina: 1