Exporting tables to csv changes file extension - vba

I'm trying to export all my tables, from my access database, to separate .csv-files. I have a loop that runs through all tables and by using TransferText I want to create a .csv-file for each table.
I am able to create a single file by writing the TransferText method.
DoCmd.TransferText acExportDelim, "ExportCsv", [Table name], filePath + "Test.csv", True
But when I'm trying to create a loop to generate a file for each table I get into trouble. (Filepath is set to desktop)
' Loops through all tables and extracts them as .csv-files
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Set db = CurrentDb
For Each tdf In db.TableDefs
' ignore system and temporary tables
If Not (tdf.Name Like "MSys*" Or tdf.Name Like "~*") Then
' Export table as CSV
'MsgBox (tdf.Name)
fileName = tdf.Name & ".csv"
DoCmd.TransferText acExportDelim, "ExportCsv", tdf.Name, filePath + fileName, True
End If
Next
Set tdf = Nothing
Set db = Nothing
Doing it like this gives me Error '3011' saying it can't find the object. Then it gives me the object name: [table name]#csv. So for some reason it changes ".csv" to "#csv".
If I remove the file extension from the file name all I get is Error 3027 saying that the object or database is read-only.
Does anyone know if there is a solution to my problem or another way to do the same thing? Or am I gonna have to go a completely different route?
EDIT:
Other tested variations
DoCmd.TransferText acExportDelim, "ExportCsv", tdf.Name, "C:/tempFile.csv", True
DoCmd.TransferText acExportDelim, "ExportCsv", tdf.Name, "C:/" & tdf.Name & ".csv", True
: Gives a "#csv" error.
DoCmd.TransferText acExportDelim, "ExportCsv", tdf.Name, "C:/tempFile", True
DoCmd.TransferText acExportDelim, "ExportCsv", tdf.Name, "C:/" & tdf.Name, True
: Gives a read only error

This is known limitation. TransferText doesn't like convoluted filenames.
So, export to a simple filename, then rename that file to its final name:
ExportFinal = "YourFinalName.csv"
ExportTemp = "FileToRename.csv"
DoCmd.TransferText acExportDelim, "ExportCsv", tdf.Name, ExportTemp, True
VBA.FileCopy ExportTemp, ExportFinal
VBA.Kill ExportTemp

So after lots of trial and error I have found a way that works for me.
With some inspiration from #Gustav I went with creating .xls files, which for some reason works. And then convert those files with a custom script to .csv-files. Then I remove the .xls files leaving only my .csv-files left.
So my loop now looks like this:
For Each tdf In db.TableDefs
' ignore system and temporary tables
If Not (tdf.Name Like "MSys*" Or tdf.Name Like "~*") Then
' Export as xls-files
fileName = tdf.Name & ".xls"
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12, tdf.Name, filePath & env & fileName, True
' Convert xls-files to .csv and remove the xls-files.
ConvertXls2CSV (filePath & env & fileName)
VBA.Kill filePath & env & fileName
End If
Next
And here is the converting code: (Credit to: https://www.devhut.net/2012/05/14/ms-access-vba-convert-excel-xls-to-csv/)
Function ConvertXls2CSV(sXlsFile As String)
On Error Resume Next
Dim oExcel As Object
Dim oExcelWrkBk As Object
Dim bExcelOpened As Boolean 'Was Excel already open or not
'Review 'XlFileFormat Enumeration' for more formats
Const xlCSVWindows = 23 'Windows CSV Format
Const xlCSV = 6 'CSV
Const xlCSVMac = 22 'Macintosh CSV
Const xlCSVMSDOS = 24 'MSDOS CSV
Set oExcel = GetObject(, "Excel.Application") 'Bind to existing instance of Excel
If Err.Number <> 0 Then 'Could not get instance of Excel, so create a new one
Err.Clear
'On Error GoTo Error_Handler
Set oExcel = CreateObject("excel.application")
bExcelOpened = False
Else 'Excel was already running
bExcelOpened = True
End If
'On Error GoTo Error_Handler
oExcel.ScreenUpdating = False
oExcel.Visible = False 'Keep Excel hidden from the user
oExcel.Application.DisplayAlerts = False
Set oExcelWrkBk = oExcel.Workbooks.Open(sXlsFile)
'Note: you may wish to change the file format constant for another type declared
'above based on your usage/needs in the following line.
oExcelWrkBk.SaveAs Left(sXlsFile, InStrRev(sXlsFile, ".")) & "csv", xlCSVWindows, Local:=True
oExcelWrkBk.Close False
If bExcelOpened = False Then
oExcel.Quit
End If
Error_Handler_Exit:
On Error Resume Next
Set oExcelWrkBk = Nothing
Set oExcel = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: ConvertXls2CSV" & vbCrLf & _
"Error Table: " & sXlsFile & vbCrLf & _
"Error Description: " & Err.Description, _
vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function

Related

Replace a string in a .csv file before import into MS Access

I need to import multiple csv files into one access table, but before the import i would like to replace ",," with ",". Is there any way to do this?
For now i've got this code that only imports the files:
Private Sub bImportFiles_Click()
On Error GoTo bImportFiles_Click_Err
Dim objFS As Object, objFolder As Object
Dim objFiles As Object, objF1 As Object
Dim strFolderPath As String
Dim ts, tse As Date
ts = Now() 'Initializare start import
'Import fisiere colectare
strFolderPath = "C:\Users\costicla\test\"
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFS.GetFolder(strFolderPath)
Set objFiles = objFolder.files
For Each objF1 In objFiles
If Right(objF1.Name, 3) = "csv" Then
DoCmd.SetWarnings False
DoCmd.TransferText acImportDelim, "specs", "ALL", strFolderPath & objF1.Name, False
'DoCmd.RunSQL "INSERT INTO COLL_ALL ( Data_Inc, CNP, CB, CN, COM, N_UNITS, PUAN, Price, SN_ACT )"
Name strFolderPath & objF1.Name As "C:\Users\costicla\import\" & objF1.Name 'Move the files to the archive folder
End If
Next
Set objF1 = Nothing
Set objFiles = Nothing
Set objFolder = Nothing
Set objFS = Nothing
'tse = Now()
DoCmd.SetWarnings True
'MsgBox ("Import done !!! start at:" & ts & " end at:" & tse)
MsgBox ("Import ALL done !!! " & _
"start at: " & ts & " end at: " & tse)
bImportFiles_Click_Exit:
Exit Sub
DoCmd.SetWarnings True
bImportFiles_Click_Err:
MsgBox Err.Number & " " & Err.Description
Resume bImportFiles_Click_Exit
End Sub
You can use VBA's File I/O operations to open a file, import all of the data in one go, replace the double commas and output it to a new file. The code below should get you started:
Sub sReplaceDoubleComma(strInFile As String)
On Error GoTo E_Handle
Dim intInFile As Integer
Dim strOutFile As String
Dim intOutFile As Integer
Dim strInput As String
intInFile = FreeFile
Open strInFile For Input As intInFile
strOutFile = "J:\test-data\temp.txt"
intOutFile = FreeFile
Open strOutFile For Output As intOutFile
strInput = Input(LOF(intInFile), intInFile)
Print #intOutFile, Replace(strInput, ",,", ",")
Close #intInFile
Close #intOutFile
' Kill strInFile
' Name strOutFile As strInFile
sExit:
On Error Resume Next
Exit Sub
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "sReplaceDoubleComma", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume sExit
End Sub
Once you are happy that this works, you can uncomment the two lines towards the end to replace the input file.
You can then call this procedure from within part of your existing code:
For Each objF1 In objFiles
If Right(objF1.Name, 3) = "csv" Then
DoCmd.SetWarnings False
Call sReplaceDoubleComma(strFolderPath & objF1.Name)
DoCmd.TransferText acImportDelim, "specs", "ALL", strFolderPath & objF1.Name, False
Name strFolderPath & objF1.Name As "C:\Users\costicla\import\" & objF1.Name 'Move the files to the archive folder
End If
Next
Link, don't import, the file, and you have a linked table.
Now, use this linked table as source in a simpel select query where you filter, modify, and convert the data and alias the fields as needed.
Then use this query as source in an append query that will add the records to your COLL_ALL table.

VBA/Microsoft Access Macro - SELECT from all tables, export results to flat files

I am trying to write a macro in Microsoft Access to accomplish the following:
For a list of linked tables (ODBC connection):
-SELECT top 10 records
-export results of each query to either Excel or CSV file of title [table_name] + suffix, i.e., "tablename1_10", "tablename2_10", etc. to a designated folder
======================
UPDATE:
======================
I now have a script, which despite my previous error with the ODBC connection, works when I do not try to declare an ODBC Connection String and declare "qdf.Open".
Macro below, with small edits for anonymity:
Sub queryAllTables()
Dim tables() As String
tables = Split("<table names>", ",")
For Each element In tables
Dim elm As String
elm = element
Call sExportTop10(elm, "<folder>", False)
Next element
End Sub
Sub sExportTop10(strTable As String, strFolder As String, blnExcel As Boolean)
On Error GoTo E_Handle
Dim strFile As String
Dim strSQL As String
If Right(strFolder, 1) <> "\" Then strFolder = strFolder & "\"
strFile = strFolder & strTable
strSQL = "SELECT TOP 10 * FROM [" & strTable & "];"
Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
For Each MyQueryDef In CurrentDb.QueryDefs
If MyQueryDef.Name = "qdfExport" Then
CurrentDb.QueryDefs.Delete ("qdfExport")
Exit For
End If
Next
Set dbs = CurrentDb()
Set qdf = dbs.CreateQueryDef("qdfExport")
qdf.ReturnsRecords = False
qdf.SQL = strSQL
qdf.OpenRecordSet
If blnExcel = True Then
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "qdfExport", strFile & "_10.xlsx", True
Else
DoCmd.TransferText acExportDelim, , "qdfExport", strFile & "_10.csv", True
End If
sExit:
On Error Resume Next
Exit Sub
E_Handle:
Msg = "Error # " & Str(Err.Number) & " was generated by " _
& Err.Source & Chr(13) & Err.Description
MsgBox Msg, , "Error", Err.HelpFile, Err.HelpContext
Resume sExit
End Sub
I would suggest that you create a procedure that does the exporting to Excel/CSV, and then you can call it each time to do the exporting. In addition, you will need a Query called "qdfExport" that you will use to modify the SQL to get the top 10 records. Some VBA would look like:
Sub sExportTop10(strTable As String, strFolder As String, blnExcel As Boolean)
On Error GoTo E_Handle
Dim strFile As String
Dim strSQL As String
If Right(strFolder, 1) <> "\" Then strFolder = strFolder & "\"
strFile = strFolder & strTable
strSQL = "SELECT TOP 10 * FROM [" & strTable & "] ORDER BY 1 ASC;"
CurrentDb.QueryDefs("qdfExport").SQL = strSQL
If blnExcel = True Then
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "qdfExport", strFile & "_10.xlsx", True
Else
DoCmd.TransferText acExportDelim, , "qdfExport", strFile & "_10.csv", True
End If
sExit:
On Error Resume Next
Exit Sub
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "sExportTop10", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume sExit
End Sub
And you would then call it as:
call sExportTop10("tblPrintID","C:\test",False)
Which would export the top 10 records from a table called "tblPrintID" to a CSV file in the folder "C:\test".
In the procedure, I have used ORDER BY 1 in the SQL, which ensures that the data is sorted by the first field, which is where the primary key is always located in my tables. If there is no order in the table, then there is no guarantee what order will be used.
Regards,

How to Import Excel File to Microsoft Access 2013

I have an Access 2013 database with all tables linked to SQL Server 2016 tables. I have an Excel 2013 (.xlsx) file, that I need to import into a a table in Ms Access that is linked to SQL Server via vba Code (all fields in xlsx and table are the same)
All my VBA code resides in the Access database, I have a form with a button with event in it, I try to use de "transferspreadsheet", an "Insert to" Clause for sql but neither of them has worked for me
Here is my code,
xtRuta2 name of the field in the form that have the path
Dim strArchivo2 String ' path of the file xlsx c:\reports\mireporte.xlsx
dim miAlerta2 as string
Dim ssql As String
strArchivo2 = txtRuta2
miAlerta2 = MsgBox("¿Do you want to import new information for " & strArchivo2 & "?" & vbCrLf & vbCrLf & "This operation will be update all the information", vbExclamation + vbOKCancel, "¡INFORMATION IMPORT ALERT!")
If miAlerta2 = vbOK Then
varAlert2 = MsgBox("Please confirm you want to import new information?", vbExclamation + vbOKCancel, "¡CONFIRMATION IMPORT ALERT!")
If varAlert2 = vbOK Then
'DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12, "tbl_ZSales_Export Worksheet", strArchivo2, True, "Export Worksheet$"
ssql = "INSERT INTO [tbl_Export Worksheet] select * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 'Excel 12.0;Database=" & strArchivo2 & ";HDR=YES', 'SELECT * FROM [Export Worksheet$)'"
'CurrentDb.Execute ssql
MsgBox "Import Finished", vbExclamation + vbOKOnly
endif
end if
Can you please help me to write the correct code for this to work
Thanks regards!
This piece of code (late bdingin interaction with excel) is used to convert excel sheet to text file and then import to a table of your choosing. I prefer to use this method as access has an annoying habit of trying ot interpret your data for you when using transferspreadsheet. With creating an import spec (which you need to do to use this method), you can easily predefine the data types.
Option Compare Database
Option Explicit
Private Sub stuff()
On Error GoTo GetAccrualFile_Err
Dim fileLoc As String
Dim path As String, Sep As String, NewTextFile As String, WholeLine As String
Dim oXL As Object, sheet As Object
Dim i As Long, j As Long, counteri As Long, counterj As Long
Dim bringOver As Variant
DoCmd.SetWarnings False
DoCmd.Hourglass True
counteri = 0
counterj = 0
Sep ="your prefered delimiter"
DoCmd.RunSQL "DELETE * FROM TBL"
fileLoc = "UNC PATH AND FILE NAME" & ".xlsx"
path = Left(fileLoc, InStrRev(fileLoc, "\") - 1) & "\"
NewTextFile = "UNC PATH AND FILE NAME" & ".txt"
Set oXL = CreateObject("Excel.Application")
With oXL
.WorkBooks.Open FileName:=path & Dir$(fileLoc)
Open NewTextFile For Output As #2
bringOver = .Worksheets("your sheet name").UsedRange 'you might need to adjust this line to get the sheet your after
For i = LBound(bringOver, 1) To UBound(bringOver, 1)
For j = LBound(bringOver, 2) To UBound(bringOver, 2)
WholeLine = WholeLine & bringOver(i, j) & Sep
counterj = counterj + 1
Next j
'used if you want to skip column headers
If counteri <> 0 Then
Print #2, WholeLine
End If
WholeLine = ""
counteri = counteri + 1
counterj = 0
Next i
counteri = 0
Erase bringOver
End With
Close #2
DoCmd.TransferText acImportDelim, "importspecname", "tbltoimportto", NewTextFile, False
'***************************************************************************************
'you will need to learn how to set up import specs, as well as understand the arguments for DoCmd.TransferText
'***************************************************************************************
CleanUp:
DoCmd.SetWarnings True
DoCmd.Hourglass False
On Error Resume Next
DoEvents
oXL.Quit
oXL.Application.Quit
If Dir(NewTextFile) <> "" Then Kill NewTextFile
Erase bringOver
DoCmd.SetWarnings True
DoCmd.Hourglass False
Exit Sub
GetAccrualFile_Err:
DoCmd.SetWarnings True
DoCmd.Hourglass False
msgbox "An error has occured. " & " " & ERR.Number & " " & ERR.Description & " "
GoTo CleanUp
Resume
End Sub
Try EPPlus, a free library which allows you to manage Excel files from .Net platform.
Here you have a tutorial: https://riptutorial.com/epplus

Access error 3043 (network access interrupted) when using module code to import

I'm new to Access as well as VB so please bear with me.
I've found and modified a small snip of code to connect to a password protected access db on the network in an attempt to import the table defs and data into a separate database (effectively a copy).
The problem I'm running into is when I execute the code I get a 3043 error and the tables are not imported. This does not happen when I use the GUI import tool through access.
does anyone know why I only receive this error when using the code (executed via macro), and how I might diagnose the issue for myself in the future?
Or perhaps a better way to automate the importing of data? This was the first method that popped in my head after a bit of digging, so if there is a better way to approach this I'm all for learning.
Code snipit for reference (if I'm using bad practices or doing something wrong please point it out):
Public Function ImportAllTbls(sExtDbPath As String, sExtDbName As String, sExtDbPass As String)
On Error GoTo Error_Handler
Dim tdf As DAO.TableDef
Dim acc As Access.Application
Dim db As DAO.Database
Dim fullDbPath As String
fullDbPath = sExtDbPath & "\" & sExtDbName
Set acc = New Access.Application
acc.Visible = True
acc.OpenCurrentDatabase fullDbPath, False, sExtDbPass
Set db = acc.CurrentDb()
For Each tdf In db.TableDefs 'Loop through all the table in the external database
If Left(tdf.Name, 4) <> "MSys" Then 'Exclude System Tables
On Error GoTo Error_Handler
acc.DoCmd.TransferDatabase acImport, "Microsoft Access", fullDbPath + ";pwd=" + sExtDbPass, acTable, tdf.Name, tdf.Name, False, False
End If
Next tdf
db.Close
Set db = Nothing
Exit Function
Error_Handler:
MsgBox "MS Access has generated the following error" & vbCrLf & vbCrLf & "Error Number: " & _
Err.Number & vbCrLf & "Error Source: ImportAllTbls" & vbCrLf & "Error Description: " & _
Err.Description, vbCritical, "An Error has Occured!"
Exit Function
End Function
not sure the acc is needed, you might try just:
DoCmd.TransferDatabase acImport, "Microsoft Access", fullDbPath + ";pwd=" + sExtDbPass, acTable, tdf.Name, tdf.Name, False, False
End If
This syntax is very suspect: + ";pwd=" + sExtDbPass
I would suggest you manually type in the path and password; rather than call it - - just as a sanity check to get at least it working..... and then do more research on the correct syntax of your Where statement regarding the PW.

How to save a given range in excel as csv?

I have data in excel columns and I want to extract first 7 columns and save it in another csv file. The file name would be in a particular format basis the information I collect from user using a form and other details such as time-stamp.
I am using the following code:
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "Select a Folder"
.AllowMultiSelect = False
.InitialFileName = "" '<~~ The start folder path for the file picker.
If .Show <> -1 Then GoTo NextCode
MyPath = .SelectedItems(1) & "\"
End With
NextCode:
With ActiveWorkbook
.SaveAs Filename:=MyPath & MyFileName, FileFormat:=xlCSV, CreateBackup:=False
Application.DisplayAlerts = False
ThisWorkbook.CheckCompatibility = False
.Close False
End With
But this writes all the columns in the output CSV and also closes the open xls (which I don't want to close).
That's a rather playful approach. Maybe not too practical, I also suspect this to be rather slow with large amounts of data.
However: if you make use of recordsets in other parts of your routine, this might be worth looking into.
Option Explicit
Sub ExportRange()
Dim mytxt As String
Dim fld As Object
With GetRecordset(ThisWorkbook.Sheets(2).UsedRange)
For Each fld In .Fields
mytxt = mytxt & fld.Name & ";"
Next fld
mytxt = mytxt & vbNewLine
While Not .EOF
For Each fld In .Fields
mytxt = mytxt & fld.Value & ";"
Next fld
mytxt = mytxt & vbNewLine
.movenext
Wend
Debug.Print mytxt
End With
Open ThisWorkbook.Path & "\test.csv" For Binary Access Write As #1
Put #1, , mytxt
Close #1
End Sub
It utilizes this function for reading ranges (.UsedRange in my example) into recordsets, without having to define ADODB-references and setting up a DB-Connection:
Function GetRecordset(rng As Range) As Object
'https://usefulgyaan.wordpress.com/2013/07/11/vba-trick-of-the-week-range-to-recordset-without-making-connection/
Dim xlXML As Object
Dim rst As Object
Set rst = CreateObject("ADODB.Recordset")
Set xlXML = CreateObject("MSXML2.DOMDocument")
xlXML.LoadXML rng.Value(xlRangeValueMSPersistXML)
rst.Open xlXML
Set GetRecordset = rst
End Function
Edit:
Open ThisWorkbook.Path & "\test.csv" For Binary Access Write As #1 creates the file (if it doesnt exist) and opens it.
Obviously, you can use something like
MyPath & "\test' & format(now, "yyyymmdd_hhmmss") & ".csv"
instead to use a File with timestamp in the folder you picked with the FolderPicker