Create an Access Database File and Import a Text File to a Table Within Using VBA
Access' 2GB file size limit has been a thorn in the side of developers for years, but it is possible to circumvent. One option is to create link tables to external text files instead of importing them, but this snippet of code explains how to create a disposable Access database to hold the data from just one table.
This doesn't increase the speed at which Access handles data and it takes up more space on the hard drive, but it does add some functionality like the ability to update records. The code below assumes that the Access file is disposable and designed to hold just one table, so it deletes the file if it already exists.
So much of this code is shamelessly recycled from the VBA snippet that imports a text file into an Access table which is included in Jimbo's VBA Automation Toolbox. It is doctored just enough to get the data into a disposable Access file.
The text files can be extracts from another team, clipboard copies from SE16 or a query from a SQL server saved directly to text. The last parameter of the function is the delimiter that defaults to Tab.
Sub TestA()
Dim y
y = ImportTextToExternalAccessDB("c:\stuff\PlantMagic\MARC.txt", "c:\stuff\PlantMagic\TestMARC.accdb", "tblMARC")
End Sub
Function ImportTextToExternalAccessDB(strFileName As String, strAccessFileName As String, strTableName As String, Optional ByVal strDelim As String = vbTab)
'This code assumes each Access file will hold just one table.
If Len(Dir(strAccessFileName)) > 0 Then
Kill strAccessFileName
End If
Dim accessApp As Access.Application
Set accessApp = New Access.Application
accessApp.DBEngine.CreateDatabase strAccessFileName, DB_LANG_GENERAL
accessApp.Quit
Set accessApp = Nothing
Dim db As Database
Set db = OpenDatabase(strAccessFileName)
Dim rs As DAO.Recordset
Dim nCurrent As Long, nFieldCount As Long, nRecordCount As Long
Dim RetVal As Variant, nCurRec As Long, dnow As Date, nCurSec As Long
Dim nTotalSeconds As Long, nSecondsLeft As Long
Dim nTotalbytes As Long, nFileLen As Long
Dim strTest As Variant
Dim strTemp As String
Dim strHeadersIn() As String
Dim strHeaders(999) As String
Const nReadAhead As Long = 300000
Dim nSizes(999) As Long, strRecords(nReadAhead) As String, nRecords As Long, nLoaded As Long
Dim strFields() As String
Dim nHeaders As Long
Dim isSAP As Boolean
nFileLen = FileLen(strFileName)
RetVal = SysCmd(acSysCmdSetStatus, "Preparing to import " & strTableName & " from " & strFileName & "...")
RetVal = DoEvents()
Open strFileName For Input As #1
Line Input #1, strTest
If Left(strTest, 6) = "Table:" Then 'This is an SAP extract!
isSAP = True
Line Input #1, strTest
Line Input #1, strTest
Line Input #1, strTest 'Fourth line has the headers!
Else
isSAP = False
End If
If InStr(1, strTest, "|", vbTextCompare) Then
strDelim = "|"
End If
nTotalbytes = nTotalbytes + Len(strTest) + 2 ' +2 for vbCrLf--This line prevents div by zero later...
strTest = Trim(strTest)
If Right(strTest, 1) = strDelim Then
strTest = Left(strTest, Len(strTest) - 1)
End If
strHeadersIn = Split(Trim(strTest), strDelim)
nHeaders = 0
For Each strTest In strHeadersIn
nHeaders = nHeaders + 1
strTest = Replace(Replace(strTest, " ", ""), ".", "")
strTest = Replace(Replace(strTest, " ", ""), ".", "")
If Len(Trim(strTest)) = 0 Then
strHeaders(nHeaders) = "HEADER" & Right("000" & nHeaders, 3)
Else
strHeaders(nHeaders) = Trim(strTest)
End If
For nCurrent = 1 To nHeaders - 1
If strHeaders(nHeaders) = strHeaders(nCurrent) Then
strHeaders(nHeaders) = strHeaders(nHeaders) & nHeaders
End If
Next
Next
strHeaders(0) = nHeaders
RetVal = SysCmd(acSysCmdClearStatus)
RetVal = SysCmd(acSysCmdInitMeter, "Preparing to import " & strTableName & " from " & strFileName & "...", nReadAhead)
RetVal = DoEvents()
Do While Not EOF(1) And nRecords < nReadAhead 'Read through the file and get the maximum sizes for fields in advance.
Line Input #1, strTest
strTest = Trim(strTest)
If Right(strTest, 1) = strDelim Then
strTest = Left(strTest, Len(strTest) - 1)
End If
If isSAP And Left(strTest, 20) = "--------------------" Then
strTest = "" 'Skip this line!
End If
If Len(strTest) > 0 Then
nRecords = nRecords + 1
strRecords(nRecords) = strTest
strFields = Split(strTest, strDelim)
nCurrent = 0
For Each strTest In strFields
nCurrent = nCurrent + 1
If Len(strTest) > nSizes(nCurrent) Then
nSizes(nCurrent) = Len(strTest)
End If
Next
If Second(Now) <> nCurSec Then
nCurSec = Second(Now)
RetVal = SysCmd(acSysCmdUpdateMeter, nRecords)
RetVal = DoEvents()
End If
End If
Loop
If CreateExternalAccessTable(strAccessFileName, strTableName, strHeaders, nSizes) Then
If isSAP Then
For nCurrent = 1 To nHeaders
If Left(strHeaders(nCurrent), 8) = "HEADER00" Then
strHeaders(nCurrent) = "" 'Don't bother importing this field.
End If
Next
End If
Set rs = db.OpenRecordset(strTableName)
nLoaded = 0
nTotalSeconds = 0
Do While Not EOF(1) Or nLoaded < nRecords
nCurRec = nCurRec + 1
If Second(Now()) <> nCurSec Then
nCurSec = Second(Now())
nTotalSeconds = nTotalSeconds + 1
'RetVal = DoEvents()
If nTotalSeconds > 3 Then
'nSecondsLeft = Int(((nTotalSeconds / nCurRec) * rs.RecordCount) * ((rs.RecordCount - nCurRec) / rs.RecordCount))
nSecondsLeft = Int(((nTotalSeconds / nTotalbytes) * nFileLen) * ((nFileLen - nTotalbytes) / nFileLen))
RetVal = SysCmd(acSysCmdRemoveMeter)
RetVal = SysCmd(acSysCmdInitMeter, "Importing " & strTableName & " from " & strFileName & "... " & nSecondsLeft & " seconds remaining.", nFileLen)
RetVal = SysCmd(acSysCmdUpdateMeter, nTotalbytes)
RetVal = DoEvents()
End If
End If
If nLoaded < nRecords Then
nLoaded = nLoaded + 1
strTest = strRecords(nLoaded)
Else
Line Input #1, strTest
End If
nTotalbytes = nTotalbytes + Len(strTest) + 2 'vbCrLf
strTest = Trim(strTest)
If Right(strTest, 1) = strDelim Then
strTest = Left(strTest, Len(strTest) - 1)
End If
If isSAP And Left(strTest, 20) = "--------------------" Then
strTest = "" 'Skip this line!
End If
If Len(strTest) > 0 Then
strFields = Split(strTest, strDelim)
nCurrent = 0
rs.AddNew
For Each strTest In strFields
nCurrent = nCurrent + 1
If Len(Trim(strHeaders(nCurrent))) > 0 Then
'rs.FIELDS(strHeaders(nCurrent)).Value = Trim(strFields(nCurrent - 1))
rs.FIELDS(strHeaders(nCurrent)).Value = Left(Trim(strFields(nCurrent - 1)), rs.FIELDS(strHeaders(nCurrent)).Size)
End If
Next
rs.Update
End If
Loop
rs.Close
End If
Close #1
RetVal = SysCmd(acSysCmdRemoveMeter)
End Function
Function CreateExternalAccessTable(strAccessFileName As String, strTableName As String, strFields() As String, nSizes() As Long) As Boolean
Dim nCounter As Long
Dim dbs As DAO.Database
'Now create the database. Rename the old database if necessary.
'Set dbs = CurrentDb
Set dbs = OpenDatabase(strAccessFileName)
Dim tdf As DAO.TableDef
Dim fld1 As DAO.Field
Dim fld2 As DAO.Field
Dim fName As String
Dim fType As Integer
Dim fSize As Integer
On Error GoTo ErrorHandler
'Check for existence of TargetTable
nCounter = 0
Do While nCounter < dbs.TableDefs.Count
If dbs.TableDefs(nCounter).Name = strTableName Then
'Delete TargetTable--must start from scratch
dbs.TableDefs.Delete (strTableName)
End If
nCounter = nCounter + 1
Loop
Set tdf = dbs.CreateTableDef(strTableName)
For nCounter = 1 To Val(strFields(0))
fName = strFields(nCounter)
fType = dbText
fSize = nSizes(nCounter) 'fSize = 255
Set fld1 = tdf.CreateField(fName, fType, fSize)
fld1.AllowZeroLength = True
fld1.Required = False
tdf.FIELDS.Append fld1
Next
'Create the table in the database
dbs.TableDefs.Append tdf
dbs.TableDefs.Refresh
CreateExternalAccessTable = True
Exit Function
ErrorHandler:
MsgBox "Error number " & Err.Number & ": " & Err.Description
CreateExternalAccessTable = False
Exit Function
End Function
Note: This snippet of code relies on several referenced DLLs. Be sure to add these in the Module to ensure that this code functions.


