21. april 2004 - 10:37Der er
17 kommentarer og 1 løsning
VBA sammenlægning af filer
Det her er vist en svær én, som nok kræver en hardcore VBA-nørd:
Jeg har en række Excel-ark i samme folder med samme kolonne-struktur. Forbruget af rækker varierer fra fil til fil. Jeg ønsker nu at sammenlægge alle filerne i én fil efter hinanden. Kan det lade sig gøre?
Jeg har tidligere brugt koden fra http://www.eksperten.dk/spm/333245 til at hente data fra enkelte celler til et hovedark, uden at åbne filerne. Måske kan man genbruge noget at teknikken.
Ja, der er kun ét ark i hver fil. Filerne skal samles på ét ark i hovedarket. Det kan selvfølgelig give problemer, hvis der er over 65.536 poster, men det tror jeg nu ikke bliver tilfældet i denne opgave. Men lad os bare sige, at hvis det er tilfældet, skal der fortsættes på ark2 etc.
En anden detalje, som også kunne være interessant var, at kolonne A på hovedarket indeholdt filnavnet fra detail-filerne.
Eks.: Detailfilerne indeholder 3 oplysninger: AAA, BBB, CCC som står i henholdsvis kolonne A, B og C. Hovedarket indeholder så 4 oplysninger: Kolonne A: Filnavn på detailfil Kolonne B: Oplysning AAA Kolonne C: Oplysning BBB Kolonne D: Oplysning CCC
Her er en løsningsmodel. Den er ikke optimeret i alle ender og kanter, men er ellers rimelig hurtig.
Forudsætninger: alle filer i samme bibliotek alle filer ens i opbygning alle arknavne er enslydende (kan dog senere tilpasses) 1. række er overskrift.
mangler: Filnavnet forekommer kun ved 1 record fra hver fil. (kan dog senere tilpasses)
Sub BatchProcess() Dim FS As FileSearch Dim FilePath As String, FileSpec As String Dim i As Integer Dim MySheet As String FilePath = "C:\mytest\" FileSpec = "*.xls" MySheet = "Sheet1" Set FS = Application.FileSearch With FS .LookIn = FilePath .Filename = FileSpec .Execute If .FoundFiles.Count = 0 Then MsgBox ("Ingen filer fundet") Exit Sub End If End With For i = 1 To FS.FoundFiles.Count ThisWorkbook.Worksheets(1).Range("B65536").End(xlUp).Offset(1, -1) = FS.FoundFiles(i) GetWorksheetData FS.FoundFiles(i), "SELECT * FROM [" & MySheet & "$];", ThisWorkbook.Worksheets(1).Range("B65536").End(xlUp).Offset(1, 0) Next End Sub
Sub GetWorksheetData(strSourceFile As String, strSQL As String, TargetCell As Range) Dim cn As ADODB.Connection, rs As ADODB.Recordset, f As Integer, r As Long If TargetCell Is Nothing Then Exit Sub Set cn = New ADODB.Connection On Error Resume Next cn.Open "DRIVER={Microsoft Excel Driver (*.xls)};DriverId=790;ReadOnly=True;" & _ "DBQ=" & strSourceFile & ";" On Error GoTo 0
Set rs = New ADODB.Recordset On Error Resume Next rs.Open strSQL, cn, adOpenStatic, adLockOptimistic, adCmdText On Error GoTo 0
TargetCell.CopyFromRecordset rs If rs.State = adStateOpen Then rs.Close Set rs = Nothing cn.Close Set cn = Nothing End Sub
P.t. er det ikke et problem med de 65536 records, men for fuldstændighedens skyld, ville det måske være rart, hvis den fortsatte på sheet2.
Den med fil-navnet ville jeg imidlertid gerne have med, selvom jeg selvfølgelig kunne lave en ekstra kolonne og så indsætte en formel som tjekker om cellen er tom, og så hente senest anvendte filnavn. Det ville dog være nemmere, hvis den var en del af koden. Oplysningen vil jeg gerne have med, så jeg f.eks. kan bruge pivot-tabel funktionen.
Sub GetWorksheetData(strSourceFile As String, strSQL As String, TargetCell As Range) Dim cn As ADODB.Connection, rs As ADODB.Recordset, f As Integer, r As Long Dim JustFileName As String If TargetCell Is Nothing Then Exit Sub Set cn = New ADODB.Connection JustFileName = Mid(strSourceFile, InStrRev(strSourceFile, "\") + 1) On Error Resume Next cn.Open "DRIVER={Microsoft Excel Driver (*.xls)};DriverId=790;ReadOnly=True;" & _ "DBQ=" & strSourceFile & ";" On Error GoTo 0
Set rs = New ADODB.Recordset On Error Resume Next rs.Open strSQL, cn, adOpenStatic, adLockOptimistic, adCmdText On Error GoTo 0 TargetCell.CopyFromRecordset rs Range(TargetCell.Offset(0, -1), TargetCell.Offset(rs.RecordCount - 1, -1)) = JustFileName If rs.State = adStateOpen Then rs.Close Set rs = Nothing cn.Close Set cn = Nothing End Sub
Ok, her kører den bare, men her er et par forbedringer I øverste sub er der fjernet en linie of i nedeste en lille fejlsikring.
Den vil stadig fejle hvis du ændrer GetWorksheetData FS.FoundFiles(i), "SELECT * FROM [" & MySheet & "$];", ThisWorkbook.Worksheets(1).Range("B65536").End(xlUp).Offset(1, 0) til at referere til kolonne A (man kan ikke offsette -1 kolonne på kolonne A)
Sub BatchProcess() Dim FS As FileSearch Dim FilePath As String, FileSpec As String Dim i As Integer Dim MySheet As String FilePath = "C:\mytest\" FileSpec = "*.xls" MySheet = "Sheet1" Set FS = Application.FileSearch With FS .LookIn = FilePath .Filename = FileSpec .Execute If .FoundFiles.Count = 0 Then MsgBox ("Ingen filer fundet") Exit Sub End If For i = 1 To FS.FoundFiles.Count GetWorksheetData FS.FoundFiles(i), "SELECT * FROM [" & MySheet & "$];", ThisWorkbook.Worksheets(1).Range("B65536").End(xlUp).Offset(1, 0) Next End With
End Sub
Sub GetWorksheetData(strSourceFile As String, strSQL As String, TargetCell As Range) Dim cn As ADODB.Connection, rs As ADODB.Recordset, f As Integer, r As Long Dim JustFileName As String If TargetCell Is Nothing Then Exit Sub Set cn = New ADODB.Connection JustFileName = Mid(strSourceFile, InStrRev(strSourceFile, "\") + 1) On Error Resume Next cn.Open "DRIVER={Microsoft Excel Driver (*.xls)};DriverId=790;ReadOnly=True;" & _ "DBQ=" & strSourceFile & ";" On Error GoTo 0
Set rs = New ADODB.Recordset On Error Resume Next rs.Open strSQL, cn, adOpenStatic, adLockOptimistic, adCmdText On Error GoTo 0 TargetCell.CopyFromRecordset rs If rs.RecordCount > 0 Then Range(TargetCell.Offset(0, -1), TargetCell.Offset(rs.RecordCount - 1, -1)) = JustFileName End If If rs.State = adStateOpen Then rs.Close Set rs = Nothing cn.Close Set cn = Nothing End Sub
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.