I am currently trying to download many .doc files that are attached to the Microsoft Access record under the "Attachment" field which is labeled as "File/Attachment". However, I need the ability to run a query (Search By Loss Incident) which I did prior and then run the macro to download ALL the attachments from multiple records. This is my code below, I need some help with it! I am getting an error of "This expression you entered has a function containing the wrong number of arguments".
Option Compare Database
Public Function SaveAttachmentsTest(strPath As String, Optional strPattern As String = "*.*") As Long
Dim dbs As DAO.database
Dim rst As DAO.Recordset
Dim rsA As DAO.Recordset2
Dim fld As DAO.Field2
Dim strFullPath As String
'Get the database, recordset, and attachment field
Set dbs = CurrentDb
Set rst = dbs.OpenRecordset("Search By Loss Incident Name")
Set fld = rst("File/Attachment")
'Navigate through the table
Do While Not rst.EOF
'Get the recordset for the Attachments field
Set rsA = fld.Value
'Save all attachments in the field
Do While Not rsA.EOF
If rsA("FileName") Like strPattern Then
'To Export the data, use the line below
strFullPath = "C:\Users\Emmanuel\Desktop\Test" & "\" & rsA("FileName")
'Make sure the file does not exist and save
If Dir(strFullPath) = "" Then
rsA("FileData").SaveToFile strFullPath
End If
'Increment the number of files saved
SaveAttachmentsTest = SaveAttachmentsTest + 1
End If
'Next attachment
rsA.MoveNext
Loop
rsA.Close
'Next record
rst.MoveNext
Loop
rst.Close
dbs.Close
Set fld = Nothing
Set rsA = Nothing
Set rst = Nothing
Set dbs = Nothing
End Function
Introduce a local variable:
<snip>
Dim SavedAttachments As Long
<snip>
' Increment the number of files saved.
SaveAttachments = SaveAttachments + 1
End If
<snip>
Set fld = Nothing
Set rsA = Nothing
Set rst = Nothing
Set dbs = Nothing
' Return the count of saved files.
SaveAttachmentsTest = SaveAttachments
End Function
Related
Overall Goal:
Pull all files from folder > format files in staging table > copy staging table to master table > kill staging table > rinse and repeat until all files have been taken from folder, formatted and put into the master table.
Issue:
I have apparently not taken into account that some of the files sent to me will have blank worksheets (rather they may have a value that says "No Data" in cell A1). When my macro hits the "No Data" or blank sheet I get a Null error (94).
What I've tried:
strF1Data = Nz(!ref_val)
strF1Data = Nz(!ref_val,"")
Suspicions:
I think I can update the SQL UPDATE line to allow Nulls, but I feel like a more efficient solution would be to skip if null. However I have tried modifying the Do Until statement and had no luck...
Possibly Worth Mentioning:
The files have multiple worksheets. I learned this the hard way in finding this error on a random worksheet between several other worksheets that did have data.
Code: (to help save some space, I'm only giving the call files bit and formatting piece, I don't think the other pieces will be of any use. However if you would like them then let me know.)
The overall macro (see next code sections for piece with error):
Sub Pull_File_into_Staging_Table()
'Process:
'1 - Loop through all files saved to specified folder making an internal list of the files
'2 - Paste one files content to staging table at a time
'3 - Format the information in the staging table
'4 - Copy formatted staging table to 1Compare Table (master table)
Dim strFile As String 'Filename
Dim strFileList() As String 'File Array
Dim intFile As Integer 'File Number
Dim filename As String
Dim path As String
DoCmd.SetWarnings False
path = "C:\Users\USER\Desktop\Test\"
Dim rs As DAO.Recordset ' Moved from below
Dim db As DAO.Database
Set db = CurrentDb
'Loop through the folder & build file list
strFile = Dir(path & "*.xls")
While strFile <> ""
'add files to the list
intFile = intFile + 1
ReDim Preserve strFileList(1 To intFile)
strFileList(intFile) = strFile
strFile = Dir()
Wend
'see if any files were found
If intFile = 0 Then
MsgBox "No files found"
Exit Sub
End If
'cycle through the list of files
For intFile = 1 To UBound(strFileList)
filename = path & strFileList(intFile)
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel8, "Stage", filename, False
Call Format_Staging_Table
Call Copy_from_Stage_to_Master
Call Clear_Staging_Table
Next intFile
DoCmd.SetWarnings True
End Sub
The piece with the issue:
Sub Format_Staging_Table()
Dim strFile As String 'Filename
Dim strFileList() As String 'File Array
Dim intFile As Integer 'File Number
Dim filename As String
Dim path As String
DoCmd.SetWarnings False
path = "C:\Users\USER\Desktop\Test\"
Dim rs As DAO.Recordset ' Moved from below
Dim db As DAO.Database
Set db = CurrentDb
CurrentDb.Execute ("ALTER TABLE Stage ADD COLUMN UPC Text, SR_Profit_Center Text, SR_Super_Label Text, SAP_Profit_Center Text, SAP_Super_Label Text;")
CurrentDb.TableDefs("Stage").Fields("F1").Name = "ref_val"
Dim ref_val As String
Set rs = db.OpenRecordset("SELECT TOP 1 ref_val FROM Stage;", dbOpenDynaset)
ref_val = rs.Fields(0).Value
rs.Close
db.Execute "DELETE FROM [Stage] WHERE ref_val = '" & ref_val & "';"
Const YOUR_TABLE_NAME As String = "Stage"
Dim SQL_UPDATE_DATA As String
SQL_UPDATE_DATA = "SELECT *, ';' & '" & ref_val & "' FROM [" & YOUR_TABLE_NAME & "] WHERE SR_Profit_Center Is Null"
Dim strF1Data As String
Dim varData As Variant
Set rs = CurrentDb.OpenRecordset(SQL_UPDATE_DATA)
With rs
Do Until .EOF
strF1Data = !ref_val
varData = Split(strF1Data, ";")
If UBound(varData) = 4 Then
.Edit
!ref_val = ref_val
!UPC = varData(0)
!SR_Profit_Center = varData(1)
!SR_Super_Label = varData(2)
!SAP_Profit_Center = varData(3)
!SAP_Super_Label = varData(4)
.Update
End If
.MoveNext
Loop
.Close
End With
Set rs = Nothing
End Sub
Also I'm aware of the extra variable pieces, I will clean it up once I get it working.
File Examples:
Working File:
CE16041901
00791558441123;US1K100017;CGR;US1K100001;UNKNOW
00791558442328;US1K100017;CGR;US1K100001;UNKNOW
00791558440720;US1K100017;CGR;US1K100001;UNKNOW
00791558444629;US1K100017;CGR;US1K100001;UNKNOW
00791558440522;US1K100017;CGR;US1K100001;UNKNOW
00791558443325;US1K100017;CGR;US1K100001;UNKNOW
Not Working File:
CE16042001
00791558334128;US1K100017;CGR;US1K100001;UNKNOW
00791558159523;US1K100017;CGR;US1K100001;UNKNOW
00602547736604;US1A100018;UR;US1A100018;US-RU
I appreciate any help. I ran with this as far as I could, but I am still very much a novice when it comes to access and vb. If you need more information or clarification please let me know and I'll do my best to provide/explain.
No need to touch the staging table functions. Simply conditionally populate the strFileList array depending if Excel workbooks' first sheet contains No Data or empty cell. Recall Access VBA has complete access to all Excel objects via COM interface or Excel VBA reference and so can iteratively open workbooks. Hence, adjust your While/Wend loop accordingly:
Sub Pull_File_into_Staging_Table()
'...same code...
Dim objXL As Object
Dim wb As Object
Set objXL = CreateObject("Excel.Application")
strfile = Dir(Path & "*.xls")
While strfile <> ""
Set wb = objXL.Workbooks.Open(Path & strfile)
If wb.Sheets(1).Range("A1") <> "No Data" AND wb.Sheets(1).Range("A1") <> "" Then
'add files to the list
intFile = intFile + 1
ReDim Preserve strFileList(1 To intFile)
strFileList(intFile) = strfile
End If
strfile = Dir()
wb.Close False
Set wb = Nothing
Wend
'...
I have a table in my access database that contains an index of project files and their associated properties. The fields include things like Filename, Filepath, Date Created, Date Modified, etc.
I'd like to create some code to loop through every record in this table and update file properties that may have changed - specifically, the date modified and file size.
The table is tblFileIndex and the relevant fields are File_Path, File_Size and Date_Modified. The filepath is the full path, including file name, to the file so it seems to me it should be pretty easy to use that field to find the file and then update the file size and date modified.
I'm not sure how to go about creating the code to loop through the table and do this though. I'd like the code to be assigned to a button on a form I have for maintenance functions as this will be run semi-frequently as part of a maintenance routine.
Below is some example code but I get Invalid use of Null errors from sFilePath = rs.Fields("File_Path") when it reaches the end of the record set.
Private Sub Command4_Click()
Dim rs As Recordset
Dim sFilePath As String
Dim oFS As Object
Set oFS = CreateObject("Scripting.FileSystemObject")
Set rs = CurrentDb.OpenRecordset("tblFileIndex")
Do While Not rs.EOF
sFilePath = rs.Fields("File_Path")
MsgBox sFilePath
rs.MoveNext
Loop
End Sub
It looks like after I sort this out Ill need to add the following:
.Edit
rs.Fields("File_Size") = oFS.GetFile(sFilePath).Size
.Update
.Edit
rs.Fields("Date_Modified") = oFS.GetFile(sFilePath).DateLastModified
.Update
rs.MoveNext
Ok, let's clean up things:
Private Sub Command4_Click()
Dim rs As Recordset
Dim sFilePath As String
Dim oFS As Object
Set oFS = CreateObject("Scripting.FileSystemObject")
Set rs = CurrentDb.OpenRecordset("tblFileIndex")
with rs
.moveFirst
do
sFilePath = .Fields("File_Path")
.Edit
.Fields("File_Size") = oFS.GetFile(sFilePath).Size
.Fields("Date_Modified") = oFS.GetFile(sFilePath).DateLastModified
.Update
.moveNext
loop until .EOF
.Close ' Always close recordsets
end with
End Sub
Alternate notation for getting/setting RecordSet field values:
' ...
with rs
.moveFirst
do
sFilePath = ![File_Path]
.edit
![File_Size] = oFS.GetFile(sFilePath).Size
' ...
.update
.moveNext
loop until .EOF
end with
' ...
end with
' ...
I get Invalid use of Null errors from sFilePath = rs.Fields("File_Path") when it reaches the end of the record set.
Since your loop is controlled by Do While Not rs.EOF, that error suggests you have a row with Null in File_Path.
See whether that error goes away when you load the recordset with only rows where File_Path is not Null.
Dim strSelect As String
strSelect = "SELECT * FROM tblFileIndex WHERE File_Path Is Not Null;"
Set rs = CurrentDb.OpenRecordset(strSelect)
Ok, I have searched and searched and have not found an answer. I have a very simple code that exports the contents of some tables into an excel workbook and then I want to save the workbook in the same directory as the database. My code works just fine on my computer, but when I try to load the database on a colleague's, the tables export just fine, but the excel workbook will not save as the given name-it just gives a prompt and asks whether you want to save Book1.xlsx If I disable the messages, it just doesn't save it at all. (His curDir is different than mine, but I checked and the workbooks aren't in there either) The database was created in Access 2013 and both computers have Access 2013 installed, although the second one (the one that's not working) also has Access and Excel 2010 installed. The workbook is opening in Excel 2013 though. I tried changing it to an .xls file and specifying the file format number in the saveas command, but that didn't work. I am including the code that works on my computer. I'm sure this is something simple, please help. Oh I should say, it worked fine the first time, but now it won't work (and I deleted the first copy, so I know its not that it doesn't want to overwrite) I'm pulling my hair out!
Public Function DeleteExcessPendragonRecords()
On Error GoTo DeleteExcessPendragonRecords_err
Dim strSQL1 As String
Dim strSQL2 As String
Dim db As DAO.Database
Dim rs1 As DAO.Recordset
Dim rs2 As DAO.Recordset
Dim oXL As Excel.Application
Dim oWB As Excel.Workbook
Dim osheet As Excel.Worksheet
Dim strSQLlinkedtables As String
Dim rs3 As DAO.Recordset
Dim currdirpath As String
currdirpath = CurDir()
'Exporting all tables into Excel Spreadsheet
Set db = CurrentDb
DoCmd.Hourglass True
Call RefreshSharePointLinks
strSQLlinkedtables = "SELECT tbl_PendragonTableNames.LocalLink FROM tbl_PendragonTableNames WHERE tbl_PendragonTableNames.ID < 15"
Set rs1 = db.OpenRecordset(strSQLlinkedtables)
Set oXL = CreateObject("Excel.Application")
oXL.Visible = True
Set oWB = oXL.Workbooks.Add
On Error Resume Next
Do Until rs1.EOF
oWB.Sheets.Add
Set osheet = oWB.ActiveSheet
osheet.Name = rs1.Fields(0)
On Error Resume Next
Dim Linkedtable As String
Linkedtable = rs1.Fields(0)
Set rs2 = db.OpenRecordset(Linkedtable)
For i = 0 To rs2.Fields.Count - 1
osheet.Cells(1, i + 1).value = rs2.Fields(i).Name
Next i
osheet.Range("A2").CopyFromRecordset rs2
rs1.MoveNext
Loop
oWB.SaveAs currdirpath & "\PendragonBackup_" & Format(Date, "yyyymmdd") & ".xlsx"
rs2.Close
rs1.Close
oWB.Close
oXL.Close
oXL.Quit
db.Close
Set oWB = Nothing
Set oXL = Nothing
Set rs2 = Nothing
Set rs1 = Nothing
Set db = Nothing
'Code continues after this, but this is the relevant part
Instead of
currdirpath = CurDir()
try:
currdirpath = CurrentProject.Path
I have created a data base that comes in an installer that runs as an epos system.
On installing it on other computers, I get a large number of errors all saying that something is missing. the file runs perfectly on my computer, but the errors stop anything from working on other computers....
the errors are as follows. each has its own popup box.
broken reference to excel.exe version 1.7 or missing.
acwztool.accde missing
npctrl.dll v4.1 missing
contactpicker.dll v1.0 missing
cddbcontolwinamp.dll v1.0 missing
cddbmusicidwinamp.dll v1.0 missing
colleagueimport.dll v1.0 missing
srstsh64.dll missing
I feel like this may because I altered the module vba library referencing so that I could run a vba code that uses excel, unfortunatly i have forgotten which librarys i have added
if it helps, the code that I added which required new references is below
Public Sub SalesImage_Click()
Dim rst As ADODB.Recordset
' Excel object variables
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet
Dim xlChart As Excel.Chart
Dim i As Integer
On Error GoTo HandleErr
' excel aplication created
Set xlApp = New Excel.Application
' workbook created
Set xlBook = xlApp.Workbooks.Add
' set so only one worksheet exists
xlApp.DisplayAlerts = False
For i = xlBook.Worksheets.Count To 2 Step -1
xlBook.Worksheets(i).Delete
Next i
xlApp.DisplayAlerts = True
' reference the first worksheet
Set xlSheet = xlBook.ActiveSheet
' naming the worksheet
xlSheet.name = conSheetName
' recordset creation
Set rst = New ADODB.Recordset
rst.Open _
Source:=conQuery, _
ActiveConnection:=CurrentProject.Connection
With xlSheet
' the field names are imported into excel and bolded
With .Cells(1, 1)
.Value = rst.Fields(0).name
.Font.Bold = True
End With
With .Cells(1, 2)
.Value = rst.Fields(1).name
.Font.Bold = True
End With
' Copy all the data from the recordset into the spreadsheet.
.Range("A2").CopyFromRecordset rst
' Format the data the numbering system has been extended to encompas up to 9,999,999 sales to cover all posibilities of sales since the last stock take
.Columns(1).AutoFit
With .Columns(2)
.NumberFormat = "#,###,###"
.AutoFit
End With
End With
' Create the chart.
Set xlChart = xlApp.Charts.Add
With xlChart
.ChartType = xl3DBarClustered
.SetSourceData xlSheet.Cells(1, 1).CurrentRegion
.PlotBy = xlColumns
.Location _
Where:=xlLocationAsObject, _
name:=conSheetName
End With
'the reference must be regotten as it is lost
With xlBook.ActiveChart
.HasTitle = True
.HasLegend = False
With .ChartTitle
.Characters.Text = conSheetName & " Chart"
.Font.Size = 16
.Shadow = True
.Border.LineStyle = xlSolid
End With
With .ChartGroups(1)
.GapWidth = 20
.VaryByCategories = True
End With
.Axes(xlCategory).TickLabels.Font.Size = 8
.Axes(xlCategoryScale).TickLabels.Font.Size = 8
End With
With xlBook.ActiveChart
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Product"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Sales"
End With
'format the size and possition of the chart
With xlBook.ActiveChart
.Parent.Width = 800
.Parent.Height = 550
.Parent.Left = 0
.Parent.Top = 0
End With
'this displays the chart in excel. excel must be closed by the user to return to the till system
xlApp.Visible = True
ExitHere:
On Error Resume Next
'this cleans the excel file
rst.Close
Set rst = Nothing
Set xlSheet = Nothing
Set xlBook = Nothing
Set xlApp = Nothing
Exit Sub
HandleErr:
MsgBox Err & ": " & Err.Description, , "There has been an error!"
Resume ExitHere
End Sub
Deployment should be less troublesome if you remove your project's Excel reference and use late binding for Excel objects.
A downside is you lose the benefit of Intellisense during development with late binding. However it's very easy to switch between early binding during development and late binding for production. Simply change the value of a compiler constant.
In the module's Declarations section ...
#Const DevStatus = "PROD" 'PROD or DEV
Then within the body of a procedure ...
#If DevStatus = "DEV" Then
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet
Set xlApp = New Excel.Application
#Else ' assume PROD (actually anything other than DEV)
Dim xlApp As Object
Dim xlBook As Object
Dim xlSheet As Object
Set xlApp = CreateObject("Excel.Application")
#End If
With late binding your code will need to use the values of Excel constants rather than the constants names. Or you can define the named constants in the #Else block for production use then continue to use them by name in your code.
I don't know what all those other reference are. Suggest you take a copy of your project, remove all those references and see what happens when you run Debug->Compile from the VB Editor's main menu. Leave any unneeded references unchecked. And try late binding for the rest. I use only 3 references in production versions of Access applications: VBA; Access; and DAO.
I'm wondering if it is possible to use VBA to store, delete and recreate relationships on tables in Access VBA? The deletion part is easy, but how to store it in such a way as to be able to restore it after it's been deleted is where I get stuck.
I originally wanted to know so that I could bulk copy certain tables from one database into another copy of that database. I ran into trouble as the ref. integrity on the tables was interfering with the inserts. I thought about trying to store then delete the relations, insert the data, then restore the relations using DAO.
After thinking about it and trying to come up with some code for it, I abandoned the idea and inserted it in a different way to avoid the issue altogether. However, after the fact, I was pondering if what I had been trying is doable.
Any thoughts?
EDIT: Here's the code I started to write.
Private Sub Save_Click()
Dim db As DAO.Database
Set db = CurrentDb
'Save db.Relations somehow as SavedRelations
End Sub
Private Sub Delete_Click()
Dim db As DAO.Database
Dim rel As DAO.Relation
Set db = CurrentDb
For Each rel In db.Relations
db.Relations.Delete (rel.Name)
Next
End Sub
Private Sub Restore_Click()
Dim db As DAO.Database
Dim rel As DAO.Relation
Dim newRel As DAO.Relation
For Each rel In SavedRelations 'Stored relations from the Save sub
Set newRel = db.CreateRelation(rel.Name, rel.table, rel.ForeignTable, rel.Attributes)
For Each fld In rel.Fields
newRel.Fields.Append fld
Next
db.Relations.Append newRel
Next
End Sub
If you make a backup copy of your database before you delete the relations, you can copy them back later.
Private Sub Restore_Click()
Dim db As DAO.Database
Dim dbBackup As DAO.Database
Dim rel As DAO.Relation
Dim newRel As DAO.Relation
Set db = CurrentDb()
Set dbBackup = OpenDatabase("C:\temp\backup.mdb")
For Each rel In dbBackup.Relations
Set newRel = db.CreateRelation(rel.Name, rel.table, rel.ForeignTable, _
rel.Attributes)
For Each fld In rel.Fields
newRel.Fields.Append newRel.CreateField(fld.Name)
newRel.Fields(fld.Name).ForeignName = _
rel.Fields(fld.Name).ForeignName
Next fld
db.Relations.Append newRel
Next rel
Set fld = Nothing
Set rel = Nothing
Set dbBackup = Nothing
Set db = Nothing
End Sub
The following code will create a classic parent to child relationship
Dim nRel As DAO.Relation
Dim db As DAO.Database
Set db = CurrentDb
Set nR = db.CreateRelation("ContactIDRI", "tblContacts", _
"tblChildren", dbRelationDeleteCascade + dbRelationLeft)
nR.Fields.Append nR.CreateField("ContactID") ' parent table PK
nR.Fields("ContactID").ForeignName = "Contact_ID" ' child table FK
db.Relations.Append nR
db.Relations.Refresh
Nice work HansUp!
I modified it slightly to allow for a late-binding file browser.
Sorry guys ... it took me a few edits to get the hang of these "code block" instructions. Hopefully it's right now:(
Function selectFile()
'Late binding version of selectFile
'No MS Office Object references needed
'''''''''''''''''''''''''''''''''''''''
'http://www.minnesotaithub.com/2015/11/solved-late-binding-file-dialog-vba-example/
Dim fd As Object
Set fd = Application.FileDialog(3)
With fd
If .Show Then
selectFile = .SelectedItems(1)
Else
End
End If
End With
Set fd = Nothing
End Function
Public Function fRestoreRelationships()
'http://stackoverflow.com/questions/4028672/storing-and-recreating-relations-in-access
Dim db As DAO.Database
Dim dbBackup As DAO.Database
Dim rel As DAO.Relation
Dim newRel As DAO.Relation
Dim strBackupPath As String
Dim Msg As String
Dim CR As String
CR = vbCrLf
Msg = ""
Msg = Msg & "This procedure restores the relationships from a previous backup." & CR & CR
Msg = Msg & "If you would like to proceed with this operation, " & CR
Msg = Msg & "Please click on the [OK] button " & CR
Msg = Msg & "Otherwise click [Cancel] to exit this pocedure."
If MsgBox(Msg, vbOKCancel, "Proceed?") = vbOK Then
strBackupPath = selectFile 'Calls a FileBrowser Dialog and returns a string value
Set db = CurrentDb()
Set dbBackup = OpenDatabase(strBackupPath)
For Each rel In dbBackup.Relations
Set newRel = db.CreateRelation(rel.Name, rel.Table, rel.ForeignTable, _
rel.Attributes)
For Each fld In rel.Fields
newRel.Fields.Append newRel.CreateField(fld.Name)
newRel.Fields(fld.Name).ForeignName = _
rel.Fields(fld.Name).ForeignName
Next fld
db.Relations.Append newRel
Next rel
End If
Set fld = Nothing
Set rel = Nothing
Set dbBackup = Nothing
Set db = Nothing
End Function