Avatar billede jonesw Nybegynder
27. december 2007 - 23:55 Der er 16 kommentarer og
1 løsning

Optimering af VBA kode

Jeg har en kolonne med tal som jeg så ønsker at skal sammenlignes så hvis der er et tal som er mere en gang på listen så skal alle dataen på den række hvor tallet er kopiere over i et andet ark.
F.eks.
1
2
3
4
5
6
3
8
3
2

Så skal de rækker hvor 2 og 3 er på kopieres over i andet ark.

Der er tale om et ark med 35000 rækker af data... så tjekket tager noget tid. VBA kan ses nedenfor, og ved en test på 5185 rækker så tog det omkring 2½ min at kører det hele igennem… Da jeg er forholdsvis ny i at programmere med VB så ville jeg høre om der var nogen som kunne komme med nogle råd til hvordan programmet kan gøres hurtigere…
Skal jeg sætte starte med at indsætte alt dataen i et array eller hvad? Håber på lidt vejledning…
Og jeg ved godt at starten af min kode ikke er særlig pæn og variabler er ikke så logisk skrevet, så hvis det er helt galt og ikke til at forstå så skal jeg nok skrive dem om…

    Dim startTal As Double
    Dim iTaeller() As Double
    Dim x As Range, x2 As Range, x3 As Range, x4 As Range, c As Range
 
    Dim y As Integer, r As Integer, tjek As Integer
    Dim taller As Long
    Dim old As String
    Dim pcrt As Integer
 
    Dim tekst As String
    Dim tl As Double, tie As Integer, ialt As Long
    Dim saml_datatb() As Range
    Dim ertaget As Boolean
   
    'Fastsætter destination for hvor at de kopierede rækker skal placeres
    Set x2 = ThisWorkbook.Worksheets("Ark2").Cells(1, 2)
    tl = 0
    taller = 0
 
    old = Application.DisplayStatusBar
    Application.DisplayStatusBar = True
   
    'Finder antallet af rækker som der bruges i statusbar.
    Set x4 = ThisWorkbook.Worksheets("Ark1").Range("B3").End(xlDown)
    ialt = x4.Row
 
    'Sætter startTal lig med værdien i den enkelte celle
    For Each x In ThisWorkbook.Worksheets("Ark1").Range("J3", Range("J3").End(xlDown)).Cells
        startTal = 0
        startTal = x.Value
        ertaget = True
       
     
        tie = 0
        'Undersøger om tallet er blevet tjekket før... Hvis det er så springes det over
        If tl > 0 Then
            For r = 0 To tl - 1

                    If startTal = iTaeller(r) Then
                        ertaget = False

                End If
            Next
        End If
        'Hvis tallet ikke er blevet tjekket før så tjekkes det.
        'Først så indsættes det i arrayet iTaeller og derefter undersøges det om tallet findes i de næste celler
        If ertaget = True Then
            ReDim Preserve iTaeller(tl)
            iTaeller(tl) = startTal
            tl = tl + 1
            tie = 0
            For Each x3 In ThisWorkbook.Worksheets("Ark1").Range(x.Address, Range(x.Address).End(xlDown)).Cells
                If x3.Value = startTal Then
             
                    ReDim Preserve saml_datatb(tie)
                    Set saml_datatb(tie) = x3
                    tie = tie + 1
             
                End If
         
            Next
            'Hvis tallet er fundet mere end 1 gang så udskrives de pågældende rækker på destinationen
            If tie > 1 Then
                For y = 0 To tie - 1
             
                    saml_datatb(y).EntireRow.Copy Destination:=x2.EntireRow
                    Set x2 = ThisWorkbook.Worksheets("Ark2").Cells((x2.Row) + 1, x2.Column)
               
                Next
            End If
        End If
        'Udskriver hvor langt at processen er nået...
        taller = taller + 1
        pcrt = (taller / ialt) * 100
        tekst = taller & " ud af " & ialt & " (" & pcrt & "%)"
        Application.StatusBar = tekst
    Next
 
 
 
    Application.StatusBar = False
    Application.DisplayStatusBar = old
Avatar billede supertekst Ekspert
28. december 2007 - 00:01 #1
Kunne en sortering inden ikke afhjælpe noget på tidsforbruget?
Avatar billede jonesw Nybegynder
28. december 2007 - 00:32 #2
Ved en manuel sortering, altså via Excels sorter stigende, så tager det cirka ½ min mindre... så jo det hjælper lidt...
Avatar billede excelent Ekspert
28. december 2007 - 08:50 #3
Hvor hurtig er din egen kode til de 35000 rækker ?

Fra Ark? til Ark?
kolonne med tal?
28. december 2007 - 08:53 #4
Din kode arbejder meget i selve regnearket, og det er altid den langsomste måde at arbejde. Array's arbejder i PC's hukommelse og er derfor meget hurtigere, så Array's istedet for For Next og For Each løkker - se denne artikel for mere info om hvordan http://www.eksperten.dk/artikler/522
Avatar billede jonesw Nybegynder
28. december 2007 - 11:50 #5
Jeg har lige prøvet at sortere med de 35000 rækker... og der kom selvfølgelig lige en fejl ved omkring nr. 33000 da jeg ikke havde sat en variable til en long men integer... det tog omkring 1½ time... Men i aften vil jeg lige prøve at lave min kode om så det alt data starter med at blive lagt ind i et array og så prøver jeg lige igen og vender tilbage med hvad hastigheden nu er...
Avatar billede excelent Ekspert
28. december 2007 - 12:51 #6
Min kode checker 5200 rækker på knap 30 sek
og 35000 på ca. 4 min

Checker Ark1 kolonne A1:A5200/35000
Findes en dublet, flyttes A?:G? til Ark2

Det var måske bedre at forklare formålet med din kode:
er det for at finde dubletter? og flytte en evt forekomst
eller er det evt. flere dubletter som skal flyttes ?
28. december 2007 - 13:41 #7
Excel kan iøvrigt selv foretage denne procedure... via
Data->Filer->Avanceret
Marker "Copy to another location"
Sæt List range
Sæt copy to (første celle)
Marker "Unique records only"
28. december 2007 - 14:15 #8
Prøv lige at tage tid på denne her:

Sub Exp812027()
    Dim wsFrom As Worksheet
    Dim wsTo As Worksheet
    Dim soArray() As Variant, lItem As Long
    Dim colUniqueValues As Collection
    Dim colDubletRows As Collection, iDublet As Integer
    Dim lCopyToNextRow As Long
   
   
    ' Assigning values to variables
    Set wsFrom = ThisWorkbook.Worksheets("Ark1")
    Set wsTo = ThisWorkbook.Worksheets("Ark2")
    Set colUniqueValues = New Collection
    Set colDubletRows = New Collection
   
    'Fastsætter destination for hvor at de kopierede rækker skal placeres
    lCopyToNextRow = wsTo.Range("B65536").End(xlUp).Row
   
    ' Fylder array og collections
    On Error Resume Next
    soArray = Range(wsFrom.Range("J3"), wsFrom.Range("J65536").End(xlUp).Address)
    For lItem = LBound(soArray, 1) To UBound(soArray, 1)
        ' Værdier sættes ind i samlingen
        colUniqueValues.Add soArray(lItem, 1), CStr(soArray(lItem, 1))
        If Err.Number = 457 Then
            ' Rækkenumre sættes ind i samlingen
            colDubletRows.Add lItem + 2 'lItem starter som 1 men data starter i række 3
            Err.Clear
        End If
    Next
    On Error GoTo 0

    ' Flytter data
    For iDublet = colDubletRows.Count To 1 Step -1
        wsTo.Rows(lCopyToNextRow).EntireRow.Value = wsFrom.Rows(colDubletRows(iDublet)).EntireRow.Value
        wsFrom.Rows(colDubletRows(iDublet)).EntireRow.Delete
        lCopyToNextRow = lCopyToNextRow + 1
    Next
End Sub
28. december 2007 - 14:17 #9
Denne række
lCopyToNextRow = wsTo.Range("B65536").End(xlUp).Row
skal lige tilføjes + 1 i enden
lCopyToNextRow = wsTo.Range("B65536").End(xlUp).Row + 1
ellers overskrives der data
Avatar billede jonesw Nybegynder
28. december 2007 - 21:26 #10
Først så ved jeg ikke helt hvilken kode som du (excelent) hentyder til... men det som jeg ønsker er at alle dubletter bliver kopieret over i et andet ark...

flemmingdahl - Din kode går godt nok hurtig... med 5000 rækker så tager det ingen tid så prøver den lige på arket med 35000 rækker i morgen.
Men skal nok også lige bruge lidt tid på at finde helt ud af hvad der foregår, for jeg kender ikke helt alle de metoder som du bruger, også skal jeg lige helt have lært at bruge Arrays i forbindelse med VBA. Men det kan godt være at jeg lige kommer med et spørgsmål til koden på et tidspunkt i løbet af de næste par dage...
Men ellers så må du jo lige smide et svar for du har jo løst opgaven...
28. december 2007 - 22:15 #11
Let forklaring
Dim soArray() As Variant - definerer en array variabel.
Jeg smider hele området i J kolonnen ind i soArray

Så er der oprettet 2 collections og der udnytter jeg, at et hvert element i en collection skal have enten ingen eller en unik nøgle
colUniqueValues.Add soArray(lItem, 1), CStr(soArray(lItem, 1))
Det betyder at collectionen fejler når der forsøges tilføjet en værdi, som allerede findes... når fejlen (457) sker er der altså tale om en dublet, som så samles op i en collection uden brug af unikke nøgler.

Så et hurtigt gennemløb af dublet collectionen og brug af værdi tildeling fremfor kopiering er også den hurtigste måde - dog kommer formler så ikke med over.

KUN KOPIERING så fjern 4 nederste linie
wsFrom.Rows(colDubletRows(iDublet)).EntireRow.Delete
28. december 2007 - 23:16 #12
:-)
29. december 2007 - 14:22 #13
Fik du en tid på den 35000 rækker?? bare for lige at kende forskellen...
Avatar billede jonesw Nybegynder
31. december 2007 - 10:22 #14
Nu har jeg prøvet med de 35000 rækker og der tager det omkring 3-4 sekunder når at jeg kun kopiere, altså har fjernet 4. nederste linje...
Så det er jo meget godt, der er bare et problem og det er at der kun kommer omkring 1000 forkomster mens med den kode at jeg har lavet kommer omkring 2000... så jeg skal lige have fundet ud af hvad der er galt... og det skal jeg nok vende tilbage med;-)
GODT NYTÅR!!!
Avatar billede jonesw Nybegynder
31. december 2007 - 10:42 #15
Ahh har fundet ud af hvorfor der er en forskel..

Det er fordi at din kode kun udskriver dubletten, mens min skriver begge to...

Det er fint nok... mange tak for hjælpen...
31. december 2007 - 13:39 #16
Fint - der skal ikke mange tilføjelser til, hvis du gerne vil have begge rækker kopieret over.
31. december 2007 - 14:54 #17
For andre interesserede kan en udgave med 1000 rækker test data findes herfra http://www.smartoffice.dk/Tips/Eksperten/Index.asp

Public Sub ExpertDemo()
    'Expert question 812027 - http://www.eksperten.dk/spm/812027
    Dim wsFrom As Worksheet
    Dim wsTo As Worksheet
    Dim soArray() As Variant, lItem As Long
    Dim colUniqueValues As New Collection
    Dim colDubletRows As New Collection, iDublet As Integer
    Dim iOneCopy As Integer, bOneOriginalCopyAllreadyMade As Boolean
    Dim lCopyToNextRow As Long

    ' Assigning values to variables
    Set wsFrom = ThisWorkbook.Worksheets("Ark1")
    Set wsTo = ThisWorkbook.Worksheets("Ark2")
    'Fastsætter destination for hvor at de kopierede rækker skal placeres
    lCopyToNextRow = wsTo.Range("B65536").End(xlUp).Row + 1



    ' Fylder array og collections
    On Error Resume Next
    soArray = wsFrom.Range(wsFrom.Range("J3"), wsFrom.Range("J65536").End(xlUp).Address)
    For lItem = LBound(soArray, 1) To UBound(soArray, 1)
        If soArray(lItem, 1) = 4241 Then
            Debug.Print lItem
        End If
        ' Værdier sættes ind i samlingen
        colUniqueValues.Add soArray(lItem, 1), CStr(soArray(lItem, 1))
        If Err.Number = 457 Then
            ' Rækkenumre sættes ind i samlingen
            colDubletRows.Add lItem + 2 'lItem starter som 1 men data starter i række 3
            Err.Clear
        End If
    Next lItem
    On Error GoTo 0



    ' Håndtering af dublet data
    ' Identificering og kopiering af første forkomster af dublet data
    For iDublet = 1 To colDubletRows.Count
        bOneOriginalCopyAllreadyMade = False
        For iOneCopy = 1 To iDublet - 1
            ' hvis der er flere dubletter af samme element må originalen kun kopieres EN gang
            If soArray(colDubletRows(iOneCopy) - 2, 1) = soArray(colDubletRows(iDublet) - 2, 1) Then
                bOneOriginalCopyAllreadyMade = True
                Exit For
            End If
        Next iOneCopy
        ' Løber igennem soArray igen for at finde første elementer af dubletterne
        If Not bOneOriginalCopyAllreadyMade Then
            For lItem = LBound(soArray, 1) To UBound(soArray, 1)
                If soArray(lItem, 1) = soArray(colDubletRows(iDublet) - 2, 1) Then
                    wsTo.Rows(lCopyToNextRow).EntireRow.Value = wsFrom.Rows(lItem + 2).EntireRow.Value
                    lCopyToNextRow = lCopyToNextRow + 1
                    Exit For
                End If
            Next lItem
        End If
    Next iDublet

    ' Kopiering af dubletterne
    For iDublet = colDubletRows.Count To 1 Step -1
        wsTo.Rows(lCopyToNextRow).EntireRow.Value = wsFrom.Rows(colDubletRows(iDublet)).EntireRow.Value
        'wsFrom.Rows(colDubletRows(iDublet)).EntireRow.Delete
        lCopyToNextRow = lCopyToNextRow + 1
    Next
End Sub
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Excel kurser for alle niveauer og behov – find det kursus, der passer til dig

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester