I've been assigned the task of importing about 180 csv files into an access 2007 database. These files have been put together over the years and will be put into 1 of 3 folders. I have not set up any data checks or restrictions to these tables (such as primary keys, validation rules, or relationships). That will be done once the data has been imported. The data contained in these files are from a survey which has changed over the years. This change has caused the fields to change. The order of them has changed or sometimes a field is there and sometimes it is not. I do have a list of all the fields possible though and what table each csv file should be imported to, and know that all these fields can be text.
Here is my problem: Not knowing what the order of the columns or if a column will exist, is it possible to run a function to import these text files into their relative tables by mapping each column in the text file to it's associated column in the access table?
Each text file has headers which is useful to see shat they actually are, but there is no text qualifier which can be very annoying when dealing with id codes consisting entirely of numbers. Below is what I've tried so far. It gets the file location from a function elsewhere, adds each filename in that location to a collection, then for each file in that collection it tries to import it into it's relative field.
'Get file names from the folder and store them in a collection
temp = Dir(location & "\*.*")
Do While temp <> ""
fileNames.Add temp
temp = Dir
Loop
'Go through each file in the collection and preccess it as needed
For Each temp2 In fileNames
If (temp2 Like "trip*") Then 'Import trip files
'Gets the data from a query 'DoCmd.RunSQL "SELECT * FROM [Text;FMT=Delimited;HDR=YES;IMEX=2;CharacterSet=437;DATABASE=" & location & "].[" & temp2 & "] As csv;"
DoCmd.TransferText acImportDelim, "Trips_Import", "tbl_Trips", location & "\" & temp2, -1
End If
If (temp2 Like "catch*") Then 'Import catch files
DoCmd.TransferText acImportDelim, "Catch_Import", "tbl_Catch", location & "\" & temp2, -1
End If
If (temp2 Like "size*") Then 'Import size files
DoCmd.TransferText acImportDelim, "Size_Import", "tbl_Size", location & "\" & temp2, -1
End If
Next temp2
You can create a SELECT * query for each CSV file and open the query as a recordset. Open another recordset for the destination table.
Then for each row in the CSV recordset, add a row to the destination recordset, loop through the CSV Fields collection, and add each CSV field value to the destination field with the same name.
This approach is independent of the order in which the fields appear in the CSV file. It also doesn't matter if the CSV file includes only a subset of the fields present in the destination table. As long as each CSV field also exists in the table, it should work (assuming compatible data types, the value satisfies validation rules/constraints, etc.).
Dim db As DAO.Database
Dim fld As DAO.Field
Dim rsDest As DAO.Recordset
Dim rsSrc As DAO.Recordset
Dim strSelect As String
Dim strTableName As String
Set db = CurrentDb
'Go through each file in the collection and preccess it as needed
For Each temp2 In fileNames
Select Case Left(temp2, 4)
Case "trip"
strTableName = "tbl_Trips"
Case "catc"
strTableName = "tbl_Catch"
Case "size"
strTableName = "tbl_Size"
Case Else
' what should happen here?
' this will trigger an error at OpenRecordset(strTableName) ...
strTableName = vbNullString
' figure out a better alternative
End Select
strSelect = "SELECT csv.* FROM " & _
"[Text;FMT=Delimited;HDR=YES;IMEX=2;CharacterSet=437;DATABASE=" & _
Location & "].[" & temp2 & "] As csv;"
Debug.Print strSelect
Set rsSrc = db.OpenRecordset(strSelect, dbOpenSnapshot)
Set rsDest = db.OpenRecordset(strTableName, dbOpenTable, dbAppendOnly)
With rsSrc
Do While Not .EOF
rsDest.AddNew
For Each fld In .Fields
rsDest.Fields(fld.Name).value = fld.value
Next
rsDest.Update
.MoveNext
Loop
.Close
End With
rsDest.Close
Next temp2
Note: This is a RBAR (row by agonizing row) approach, so the performance will be less than stellar. However, I presumed you will do this only once, so the performance hit will not be a deal-breaker. If you need a faster set-based approach instead, you can build and execute an "append query" for each CSV file. To do that, you would first need to get the CSV field names, and then build the appropriate INSERT INTO statement.
Related
I'm trying to import a CSV file that is created from a web form I developed. When the form submits it creates a record in my CSV with a multitude of customer information.
As per requirements I needed to put it into a CSV, and then separately have it import into an Access database for others to use (Two steps required for server security).
The way I'm trying to do it is with a simple form with a button on it inside Access, that simply says Import, that will pull an update of the CSV whenever the user needs it.
My error is confusing me as it's stating
"Field 'F1' doesn't exist in destination table 'Applications' "
I do not have a field in my CSV labeled F1, or even any record that contains 'F1', and there is no field named F1 in my access table Applications (obviously).
Here is my VB module code from Access
Option Compare Database
Sub ImportingCSV()
Function Import()
On Error GoTo Macro1_Err
DoCmd.TransferText acImportDelim, "", "Applications", "C:\Users\ALee\Documents\formTesting22.csv", False, ""
Import:
Exit Function
Macro1_Err:
MsgBox Error$
Resume Macro1_Exit
End Function
And here is my CSV file format (spaced out for your readability)
OPUCN#WVQNAJT4PD,
2017.05.03,
test,
v,
90545452929,
4062033985,
No,
VM#TEST.VMTEST,
10003937683827,
test,
test,
689 395 3967,
2048 2983999,
No,rle#don.ca,
111 e Streeth south,
12,
Temporary,
Commercial,
100,
200,
300,
208/120V,
Three-Phase,
Underground (UG),
Ganged Position*,
23,
"dsbsdhfbslhfbshfbsdhlfbgshdfgsfslfgljshgfljshgfljshgflsj"
The error is telling me that the field for the second phone number ("4062033985" in the CSV) doesn't have a field in the table Applications, but it does! "F1" in the CSV is Customer Mobile. When I import manually through Access's import wizard this works fine.
Hopefully someone can point me in the right direction, not familiar with VB script or macros in access.
Don't import the file.
Link the csv file as a table. Then create a query to read and convert (purify) the data.
Use this query as source for further processing of the date like appending data to other tables.
a CSV file is a spreadsheet... try...
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12Xml,[YourDestinationTable],"C:\YourFileDirectoryPath, filename, and extension",true,[Spreadsheet name if multiple sheet names]
There are all kinds of ways to do this sort of thing. This is certainly the simplest method.
Private Sub Command0_Click()
DoCmd.TransferText acImportDelim, "", "Book1", "C:\your_path_here\Book1.csv", True, ""
End Sub
Let's say you want to import several CSV files, all of the same type, into the same table. Just run the script below.
Option Compare Database
Option Explicit
Private Sub Command0_Click()
DoImport
End Sub
Function DoImport()
Dim strPathFile As String
Dim strFile As String
Dim strPath As String
Dim strTable As String
Dim blnHasFieldNames As Boolean
' Change this next line to True if the first row in CSV worksheet
' has field names
blnHasFieldNames = True
' Replace C:\Documents\ with the real path to the folder that
' contains the CSV files
strPath = "C:\your_path_here\"
' Replace tablename with the real name of the table into which
' the data are to be imported
strFile = Dir(strPath & "*.csv")
Do While Len(strFile) > 0
strTable = Left(strFile, Len(strFile) - 4)
strPathFile = strPath & strFile
DoCmd.TransferText acImportDelim, , strTable, strPathFile, blnHasFieldNames
' Uncomment out the next code step if you want to delete the
' EXCEL file after it's been imported
' Kill strPathFile
strFile = Dir()
Loop
End Function
You can do all kinds of other thins too; navigate to a file using the msoFileDialogFilePicker; you can loop through record sets and load them, one by one, into your table. As Gustav suggested, you can link to your file (staging) and write records into a table (production). You should probably try all of these methods, and play around with
I am developing a database program and part of the program is the ability fr the user to attach files. These files are then copied to a specified folder and renamed to reflect the ID of the table. However, my question is that I can achieve this but when the user attaches second file for the same ID it throws error for file already exists.
So could anyone put me in the right direction on how to achieve this i.e. If file exits then save second file an incremental number soon.
some code to help to understand what i am trying to achieve:
Dim objFSO
Const OVER_WRITE_FILES = True
Set objFSO = Nothing
Dim FileLocation As String
Dim DestLocation As String
Dim fileName As String
Dim fileNameI As String
Dim iTemp As Integer
Set objFSO = CreateObject("Scripting.FileSystemObject")
FileLocation = Me.lblTempLocation.Caption
DestLocation = "C:\Dev\"
fileName = [TestName].Value
'If the backup folder doesn't exist, create it.
If Not objFSO.FolderExists(DestLocation) Then
objFSO.CreateFolder (DestLocation)
End If
'Copy the file as long as the file can be found
If Not objFSO.FileExists(DestLocation & fileName & ".pdf") Then
objFSO.CopyFile FileLocation, DestLocation & fileName & ".pdf", OVER_WRITE_FILES
Else
objFSO.CopyFile FileLocation, DestLocation & fileNameI & ".pdf", OVER_WRITE_FILES
Do While (fileName) <> vbNullString
fileNameI = fileName & Format$(iTemp, "_00")
iTemp = iTemp + 1
Loop
End If
the above works fine first time but when the file exists and it goes through the loop it throughs a stack overflow error and stops the code.
Use ID key of first table as foreign key relationship to a second tables 'FKeyID' (one to many table). Create an 'AttachID' in the second table and set your VBA to rename the file based on the attachID allowing for multiple attaches per first table ID.
AttachID = second tables unique key
FKeyID = relation with ID from first table.
I have researched the code for this online for weeks but still cannot find what I need so any help will be greatly appreciated:
I need to:
1) Import the first CSV in "C:\Documents" and create a table called "Data" with import specs I have already created.
2) Create and export all fields in a (SELECT?) query (Sport = Football, Event = Match Odds) to an excel binary file called "Match Odds 001"
I then wish to delete the data contained in this query from the Table "Data".
3) Repeat step 2) two hundred times with different Events exporting each query to an excel binary file with the name "(Event) 001" then deleting that query data from the table.
4) After all queries have run and been deleted, any remaining data in the table will be exported to an excel binary file named "Misc 001" and then this data deleted from the Table "Data" (or maybe even delete the Table "Data" completely.)
5) Repeat from 1) importing the 2nd CSV file from "C:\Documents" and exporting queries to Excel Binary files named "(Event) 002" so as not to replace the previous files.
This would continue until all CSVs have been imported and split.
As there are 200 queries I'd prefer to create them in VBA code.
I have just started using VBA in Access and so far have found code which will import all csv files in a folder so I am hoping to insert code to create, export then delete the queries.
Any help on the vba coding required is hugely appreciated.
.
.
UPDATE: With massive thanks to Mike I now have the following code but a few small issues have surfaced.
1) How can I compact the Database to reduce the filesize after the "DoCmd.RunSQL "DROP TABLE Data"" command?
I wish to repeatedly import 500MB CSV's then delete them so after I import and drop the first then import the second the filesize becomes 1GB and is increasing.
2) How difficult is it to include a second column in the Events Table so that my SELECT query becomes WHERE Event=Event & Selection=Selection and to combine both these fields to create the filename?
3) The Events Table used to create the Queries and file names sometimes contains characters that cannot be used in file names, for Example "/" & "?". Can these be easily dropped to create the filenames or might it be better to add a further column to the Events Table which would contain the filename to be used (ie a combination of Event and Selection but with the disallowed characters removed)
If I can solve these issues I will have the perfect code for my needs, again with all credit to Mike.
Sub ImportAndSplit()
Dim fileCounter As String
Dim rs As New ADODB.Recordset
Dim sql As String
Dim qdf As QueryDef
Dim file As String
Const strPath As String = "C:\Users\Robbie\Documents\Data\" 'Directory Path
Dim strFile As String 'Filename
Dim strFileList() As String 'File Array
Dim intFile As Integer 'File Number
strFile = Dir(strPath & "*.csv") 'Loop through the folder & build file list
While strFile <> ""
intFile = intFile + 1 'add files to the list
ReDim Preserve strFileList(1 To intFile)
strFileList(intFile) = strFile
strFile = Dir()
Wend
If intFile = 0 Then 'see if any files were found
MsgBox "No files found"
Exit Sub
End If
DoCmd.SetWarnings False
Set qdf = CurrentDb.QueryDefs("Export")
sql = "SELECT Event FROM Events"
rs.Open sql, CurrentProject.Connection, adOpenStatic, adLockReadOnly
For intFile = 1 To UBound(strFileList) 'cycle through the list of files & import to Access creating a new table called Data
DoCmd.TransferText acImportDelim, "Data", "Data", strPath & strFileList(intFile)
fileCounter = Format(intFile, "000") 'format i so that when you use it in file names, the files sort intuitively
Do While Not rs.EOF
sql = "SELECT * FROM Data WHERE Event='" & rs!Event & "'" 'select the records to export and export them
file = "C:\Users\Robbie\Documents\Data Split\" & rs!Event & " " & fileCounter & ".xlsb" 'use the counter to distinguish between which csv your exporting from
qdf.sql = sql
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12, qdf.Name, file
sql = "DELETE FROM Data WHERE Event='" & rs!Event & "'" 'delete the records from the source table
DoCmd.RunSQL sql
rs.MoveNext
Loop
rs.MoveFirst
'export remaining data
file = "C:\Users\Robbie\Documents\Data Split\Misc " & fileCounter & ".xlsb" 'use the counter to distinguish between which csv your exporting from
sql = "SELECT * FROM Data"
qdf.sql = sql
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12, qdf.Name, file
'delete remaining data
'DoCmd.RunSQL "DELETE FROM Data"
'or delete the table
DoCmd.RunSQL "DROP TABLE Data"
Next
MsgBox UBound(strFileList) & " Files were Imported and Split"
rs.Close
DoCmd.SetWarnings True
End Sub
Create a table with your Events in it (in my example it's called "Events" and has one field "Event").
Create a query called "Export". Doesn't matter what it does, we're going to overwrite it 200 times anyway.
Add the ActiveX Data Objects Library reference
Code:
Dim i As Integer
Dim fileCounter As String
Dim rs As New ADODB.Recordset
Dim sql As String
Dim qdf As QueryDef
Dim file As String
DoCmd.SetWarnings False
Set qdf = CurrentDb.QueryDefs("Export")
sql = "SELECT Event FROM Events"
rs.Open sql, CurrentProject.Connection, adOpenStatic, adLockReadOnly
For i = 1 To x 'where x is however many csvs you're importing
fileCounter = Format(i, "000") 'format i so that when you use it in file names, the files sort intuitively
'run your import code here
Do While Not rs.EOF
'select the records to export and export them
sql = "SELECT * FROM Data WHERE Event='" & rs!Event & "'"
file = "C:\Documents\" & rs!Event & fileCounter & ".xlsb" 'use the counter to distinguish between which csv your exporting from
qdf.sql = sql
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12, qdf.Name, file
'delete the records from the source table
sql = "DELETE FROM Data WHERE Event='" & rs!Event & "'"
DoCmd.RunSQL sql
rs.MoveNext
Loop
rs.MoveFirst
'export remaining data
file = "C:\Documents\MISC" & fileCounter & ".xlsb" 'use the counter to distinguish between which csv your exporting from
sql = "SELECT * FROM Data"
qdf.sql = sql
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12, qdf.Name, file
'delete remaining data
DoCmd.RunSQL "DELETE FROM Data"
'or delete the table
'DoCmd.RunSQL "DROP TABLE Data"
Next i
rs.Close
DoCmd.SetWarnings True
Make sure none of those files exist before running this, it will not overwrite files. Either delete them manually or you could use the Windows Script Host in the loop to delete the files if they exist before creating them...
Dim fso As New FileSystemObject
If fso.FileExists(file) Then
fso.DeleteFile (file)
End If
In response to your updates:
Consider linking them instead of importing (DoCmd.TransferText acLink, "Data"...). I don't believe it's possible to compact while you're working in a db, it would have to close.
You basically answered your own question. If you want to do that, add the column to the table and update the SQL exactly how you said.
You could use the Replace function but you'd have to run it for each illegal character and you might end up missing one. It's probably best to do what you suggested and just create them yourself in a new column.
I have an Access 2010 database that is going to import data from an excel sheet and append that data to the proper table. The issue I am having is that once imported, the data will not have the customer ID attached to it so the cust_ID field in the table will be null. What I need is a macro that will ask the user who the customer is for each import and then find that customer's ID and put that value into all of the null fields. I have made a lot of different attempts at this (mostly though the macro builder I don't even know where to start with the VBA for this) none of which have shown any results. I think my latest one is close but for some reason it isn't running after the import. I have an "After Insert" macro, and the idea was that it would prompt the user for [Customer Name]
look-up the custID from TBL_Customers then insert that into each of the null fields. Unfortunately after I import my data nothing happens and I'm really not sure why.
Parameters Name: Customer Name
Look Up A Record In TBL_Customers
Where Condition =[TBL_Customers].[FLD_Customer_Name]=[Customer Name]
Alias custName
Look Up A Record In TBL_Customers
Where Condition =[TBL_Customers].[FLD_Customer_PK_ID]=[custName]
Alias custID
For Each Record In TBL_Contacts
Where condition =IsNull([FLD_Contact_FK_Customer_ID])
EditRecord
SetField
Name FLD_Contact_FK_Customer_ID
Value =[custID]
End EditRecord
Another possibility I was thinking would be to have another column in the excel sheet with the customer's name and then Access would just have to look up the cust_ID based on that column. My issue there is I don't know how I would tell Access to import the customer name from the excel sheet find that customer's ID and insert that ID into the proper table, which is why I was leaning toward the first option.
As I said in the comments I've tried a lot of different things but I'm fairly new to VBA/Access and am running out of ideas to try.
Not sure which would be easier/better so any ideas/suggestions/recommendations are greatly appreciated.
Here is a sample of something that might get you going in the right direction. In my case I had a similar situation, importing from excel, but often one piece of data was missing. What I did was had the "users" save the excel docs into subfolders with the customer id's as part of the folder names. Using the Scripting library you can recurse through the subfolders, and I use a custom FindCustomerID function to parse the ID you are looking for from the path name. Once the import happens, the customer id can be added in.
Function ImportBlah(importDir As String, tbl As String)
Dim importPath As String
Dim fs As Scripting.FileSystemObject
Dim fo, x AS Folder
Dim fi AS File
Dim custID As String
Set fs = CreateObject("Scripting.FileSystemObject")
With fs
Set fo = .GetFolder(importDir)
For Each x In fo.SubFolders
For Each fi In x.Files
If Not (fi.Attributes Mod 8 >= 4) Then 'If not a system file
DoCmd.TransferText acImportDelim, "import_specs", tbl, fi, True
custID = FindCustomerID(x.Path)
CurrentDb.Execute "UPDATE " & tbl & " SET FileName = '" _
& fi.ParentFolder.name & "\" & fi.name & "' WHERE FileName IS NULL", _
dbFailOnError 'Add filename to Filename column
CurrentDb.Execute "UPDATE " & tbl & " SET custID = '" & _
custID & "' WHERE custID IS NULL", dbFailOnError 'Add custID to custID column
.MoveFile fi, x & "\Imported\"
End If
Next
Next
End With
End Function
This code requires the reference to the Microsoft Scripting Runtime be enabled.
I couldn't say how to do this with Access macros, but it isn't too hard to do in VBA.
I'd write the macro in excel - import the activex data objects 2.8 library reference.
Start by declaring the connection object - this works for a MySQL server we have set up, you can use the JET driver if you're just connecting over a local network.
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Set cnn = New ADODB.Connection
cnn.CursorLocation = adUseServer
cnn.Open "Driver={MySQL ODBC 5.2a Driver};Server=myserver;database=my_db;uid=user;pwd=P#ssword"
Set rst = New ADODB.Recordset
rst.CursorLocation = adUseServer
When you have the customer name (Get the name from the user, maybe by an inputbox) ping the DB to retrieve the id and save it.
rst.open "Select id from customers where customername = '" & userSubmittedName & "'",
cnn,adopenforwardonly,adlockbatchoptimistic
dim customerID as integer
customerID = rst.fields("id").Value
Then it's just a matter of inputting the data. Access has the DoCmd.RunSQL command, but in Excel you'd use ADO recordsets. It works like this -
rst.open "Select * from Orders",cnn,adopenforwardonly,adlockbatchoptimistic
Fo r x = 2 to rowCount 'Once you have the number of rows you're inputting saved obviously
rst.addnew
rst.fields("ID") = customerID
rst.fields("OrderAmount") = range("A" & x).value
rst.fields("SaleAmount") = range("B" & x).value
....
rst.update
next x
i need procedure in VBA to import data into access from csv excel file without some records,, as header and footer. Example,,, i have table in csv file, which contains some
sentence which not belong table date
A1 this is some sentence title.......
A2 title
A3.......
A7 DATA DATA DATA DATA DATA
A8 rows DATA DATA DATA DATA DATA
......
....
A256 DATA DATA DATA DATA
A257 this is some sentence
My Acess shoud contain only rows between A7 to A256. Does anyone knows procedure or whatever in VBA who solves my problems ?
thanks a lot
Edit
The easiest way to do it is to link the CSV-file into the Access database as a table. Then you can work on this table as if it was an ordinary access table, for instance by creating an appropriate query based on this table that returns exactly what you want.
You can link the table either manually or with VBA like this
DoCmd.TransferText TransferType:=acLinkDelim, TableName:="tblImport", _
FileName:="C:\MyData.csv", HasFieldNames:=true
Update
Dim db As DAO.Database
' Re-link the CSV Table
Set db = CurrentDb
On Error Resume Next: db.TableDefs.Delete "tblImport": On Error GoTo 0
db.TableDefs.Refresh
DoCmd.TransferText TransferType:=acLinkDelim, TableName:="tblImport", _
FileName:="C:\MyData.csv", HasFieldNames:=true
db.TableDefs.Refresh
' Perform the import
db.Execute "INSERT INTO someTable SELECT col1, col2, ... FROM tblImport " _
& "WHERE NOT F1 IN ('A1', 'A2', 'A3')"
db.Close: Set db = Nothing
Your file seems quite small (297 lines) so you can read and write them quite quickly. You refer to Excel CSV, which does not exists, and you show space delimited data in your example. Furthermore, Access is limited to 255 columns, and a CSV is not, so there is no guarantee this will work
Sub StripHeaderAndFooter()
Dim fs As Object ''FileSystemObject
Dim tsIn As Object, tsOut As Object ''TextStream
Dim sFileIn As String, sFileOut As String
Dim aryFile As Variant
sFileIn = "z:\docs\FileName.csv"
sFileOut = "z:\docs\FileOut.csv"
Set fs = CreateObject("Scripting.FileSystemObject")
Set tsIn = fs.OpenTextFile(sFileIn, 1) ''ForReading
sTmp = tsIn.ReadAll
Set tsOut = fs.CreateTextFile(sFileOut, True) ''Overwrite
aryFile = Split(sTmp, vbCrLf)
''Start at line 3 and end at last line -1
For i = 3 To UBound(aryFile) - 1
tsOut.WriteLine aryFile(i)
Next
tsOut.Close
DoCmd.TransferText acImportDelim, , "NewCSV", sFileOut, False
End Sub
Edit re various comments
It is possible to import a text file manually into MS Access and this will allow you to choose you own cell delimiters and text delimiters. You need to choose External data from the menu, select your file and step through the wizard.
About importing and linking data and database objects -- Applies to: Microsoft Office Access 2003
Introduction to importing and exporting data -- Applies to: Microsoft Access 2010
Once you get the import working using the wizards, you can save an import specification and use it for you next DoCmd.TransferText as outlined by #Olivier Jacot-Descombes. This will allow you to have non-standard delimiters such as semi colon and single-quoted text.