Storing and recreating relations in Access - ms-access

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

Related

Microsoft Access Database - Downloading ALL attachments from MULTIPLE entries

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

Need to hide a column in a table on button click

I have a button that calls a macro which opens up a table. I'd like to hide a column in that table when it opens. The macro opens the table tChart1_Reportable and I'd like to hide the ID column.
I figure this has to be done in VBA with the RunCode action selected in the macro.
I haven't had luck finding code that works. Found this online but it doesnt work in the current form:
Public Sub SetColumnHidden()
Dim dbs As DAO.Database
Dim fld As DAO.Field
Dim prp As DAO.Property
Const conErrPropertyNotFound = 3270
' Turn off error trapping.
On Error Resume Next
Set dbs = CurrentDb
' Set field property.
Set fld = dbs.TableDefs!tChart1_Reportable.Fields!ID
fld.Properties("ColumnHidden") = True
' Error may have occurred when value was set.
If Err.Number <> 0 Then
If Err.Number <> conErrPropertyNotFound Then
On Error GoTo 0
MsgBox "Couldn't set property 'ColumnHidden' " &; _
"on field '" &; fld.Name &; "'", vbCritical
Else
On Error GoTo 0
Set prp = fld.CreateProperty("ColumnHidden", dbLong, True)
fld.Properties.Append prp
End If
End If
Set prp = Nothing
Set fld = Nothing
Set dbs = Nothing
End Sub
Although you really should think about using a query to accomplish this goal instead, the problem with this code is definitely that the Table itself does not refresh the hidden property on execution.
That being said, this would likely suffice as a solution to this problem:
Public Sub SetColumnHidden()
Dim dbs As DAO.Database
Dim fld As DAO.Field
Dim prp As DAO.Property
Set dbs = CurrentDb
'Set field property.
Set fld = dbs.TableDefs!tChart1_Reportable.Fields!ID
fld.Properties("ColumnHidden") = True
DoCmd.Close acTable, "tChart1_Reportable"
DoCmd.OpenTable "tChart1_Reportable"
Set prp = Nothing
Set fld = Nothing
Set dbs = Nothing
End Sub
To work this through a query, create a new query with Query Design, open it in SQL view. Your query should like something like this:
SELECT [Field1],
[Field2],
[Field3]
FROM tChart1_Reportable
Basically, add every field you want to view (by the field's name), removing the "ID" field from the list. This will accomplish what you're attempting to do. As far as why the code is not working for you, all I can say is it should be.

Identifying where a table begins in a Word doc

I have a specific work doc that is produced daily which includes a paragraph of text followed by a table with a bunch of customer data. I need to import that data into an Access table.
I've found code, which I'll include below, which does just that. However it's not working as intended. Rather it's not working at all. I anticipate that it's because the word doc begins not with a table, but text.
So I have two options. 1) Find a way to format each document so it contains only the table (I'll have to automate this because we receive dozens of these files each day) or 2) adjust the code so that it detects only the table in the doc.
Is there a good way of doing either of these things?
Option Compare Database
Private Sub cmdImport_Click()
Dim appWord As Word.Application, doc As Word.Document
Dim dbs As DAO.Database, rst As DAO.Recordset, strDoc As String
Set appWord = CreateObject("Word.Application") 'establish an instance of word
strDoc = CurrentProject.Path & "\cmoSheet.docx" 'set string to document path and file
Set doc = appWord.Documents.Open(strDoc) 'establish the document
Set dbs = CurrentDb 'establish the database to use (this is our current Database)
Set rst = dbs.OpenRecordset("cmoSheetTbl") 'establish the recordset
With doc.Tables(1) 'target table 1 in cmoSheet.docx
For i = 2 To .Rows.Count 'cycle through rows in Tables(1) [we skip the first row because the table has headers]
With rst
.AddNew 'creating a new record
![ReviewerName] = doc.Tables(1).Cell(i, 1).Range.Text
![ProductDesc] = doc.Tables(1).Cell(i, 2).Range.Text
![NPI] = doc.Tables(1).Cell(i, 3).Range.Text
![LastName] = doc.Tables(1).Cell(i, 5).Range.Text
![FirstName] = doc.Tables(1).Cell(i, 6).Range.Text
![ProviderType] = doc.Tables(1).Cell(i, 7).Range.Text
![Specialty] = doc.Tables(1).Cell(i, 8).Range.Text
![BatchID] = doc.Tables(1).Cell(i, 9).Range.Text
![AdditionalDocs?] = doc.Tables(1).Cell(i, 10).Range.Text
.Update 'update the whole record
End With
Next 'go to next row in Tables(1)
End With
rst.Close: Set rst = Nothing 'close and clear recordset
db.Close: Set rst = Nothing 'close and clear database
doc.Close: Set doc = Nothing 'close and clear document
appWord.Quit: Set appWord = Nothing 'close and clear MS Word
End Sub
A nested With must be related to the outer With. In addition, adding Option Explicit will reveal a couple of errors in your code.
db.Close: Set rst = Nothing
should be:
dbs.Close: Set dbs= Nothing
Since you create an early binding to Word when declaring the variables, you can simply use the New keyword to create an instance:
Dim appWord As Word.Application, doc As Word.Document
Set appWord = New Word.Application
If you want to create a late bind to Word, remove the reference to it and declare the variable(s) as Object:
Dim appWord As Object, doc As Object
Set appWord = CreateObject("Word.Application")
Try this:
Private Sub cmdImport_Click()
Dim appWord As Word.Application, doc As Word.Document
Dim dbs As DAO.Database, rst As DAO.Recordset, strDoc As String
Set appWord = New Word.Application 'establish an instance of word
strDoc = CurrentProject.Path & "\cmoSheet.docx" 'set string to document path and file
Set doc = appWord.Documents.Open(strDoc) 'establish the document
Set dbs = CurrentDb 'establish the database to use (this is our current Database)
Set rst = dbs.OpenRecordset("cmoSheetTbl") 'establish the recordset
With doc.Tables(1) 'target table 1 in cmoSheet.docx
Dim i As Integer
For i = 2 To .Rows.count 'cycle through rows in Tables(1) [we skip the first row because the table has headers]
rst.AddNew 'creating a new record
rst![ReviewerName] = .Cell(i, 1).Range.Text
rst![ProductDesc] = .Cell(i, 2).Range.Text
rst![NPI] = .Cell(i, 3).Range.Text
rst![LastName] = .Cell(i, 5).Range.Text
rst![FirstName] = .Cell(i, 6).Range.Text
rst![ProviderType] = .Cell(i, 7).Range.Text
rst![Specialty] = .Cell(i, 8).Range.Text
rst![BatchID] = .Cell(i, 9).Range.Text
rst![AdditionalDocs?] = .Cell(i, 10).Range.Text
rst.Update 'update the whole record
Next 'go to next row in Tables(1)
End With
rst.Close: Set rst = Nothing 'close and clear recordset
dbs.Close: Set dbs = Nothing 'close and clear database
doc.Close: Set doc = Nothing 'close and clear document
appWord.Quit: Set appWord = Nothing 'close and clear MS Word
End Sub

Access 2010 VBA to put one field from a query in a variable

In Access 2010 I need to be able to click a command button that will run a query that returns a small two field recordset. Then put the second field in that recordset into a string variable.
This string variable is a link to a word document on the network. the second part of the code will then open the word document.
Any help is GREATLY appreciated.
I am getting the Error: "Object variable or With block variable not set"
My Code looks like this:
`Option Compare Database
Private Sub cmdCESpec_Click()
On Error GoTo Err_cmdCESpec_Click
Dim db As Database
Dim rs As DAO.Recordset
Dim s As String
Dim specSheet As String
s = "SELECT p.PartNum, p.CE_SpecSheet FROM tblParts p WHERE p.PartNum = '" & [Forms]![frmSpecSheet]![cboPartNum] & "'" 'Chooses the correct Spec Sheet.
Set rs = db.OpenRecordset(s)
specSheet = rs.Fields("CE_SpecSheet") 'Chooses the Spec Sheet Field
rs.Close
Dim oApp As Object
Set oApp = CreateObject("Word.Application")
oApp.Visible = True
With oApp
.Documents.Open (specSheet)
End With
Exit_cmdCESpec_Click:
Exit Sub
Err_cmdCESpec_Click:
MsgBox Err.Description
Resume Exit_cmdCESpec_Click
End Sub
After dissecting your code.. this:
"..WHERE (((tblParts.PartNum)=[Forms]![frmSpecSheet]![cboPartNum]));"
embeds the words Forms!etc into your sql-statement, it doesn't insert the combo-box value into the statement.
You need to take the form reference out of the string:
Dim db As Database
Dim rs As Recordset
Dim s As String
Dim specSheet As String
s = "SELECT tblParts.PartNum, tblParts.CE_SpecSheet FROM tblParts WHERE " _
& "tblParts.PartNum=" & [Forms]![frmSpecSheet]![cboPartNum]
'Chooses the Correct Spec Sheet
Set db = CurrentDb
Set rs = db.OpenRecordset(s)
specSheet = rs.Fields("CE_SpecSheet")
'Chooses the Spec Sheet Field
rs.Close
Dim oApp As Object
Set oApp = CreateObject("Word.Application")
oApp.Visible = True
With oApp
.Documents.Open ("specSheet")
End With
If the combobox's value is text then you'll need to also surround this with apostrophes.
You also don't need to SELECT the field tblParts.PartNum, you can just refer to it in the WHERE clause.
You haven't posted much information but what I gather you're looking for is Recordset
Dim db As Database
Dim rs As Recordset
Dim s As String
Dim myString As String
s = "SELECT * FROM myTable1;" 'Replace with the SQL you need
Set db = CurrentDb
Set rs = db.OpenRecordset(s)
myString = rs.Fields("myFieldName1") 'Replace myFieldName with the appropriate field name
rs.Close

MS ACCESS 2007 VBA : DAO recordset....how can I view all the "fields" in the returned collection

so if i do a SQL statement like so:
sql = "SELECT * FROM tblMain"
set rs = currentdb.openrecordset(sql)
what method can i use to view every "field name" in this collection i have just created. i am getting some very strange error stating that the item is not found in this collection.
i know the field exists in the table, i have triple checked the spelling everywhere when i reference it, and the SQL should be pulling everything, but i want to see it.
is there a debug.print method to see all these fields
thanks
Justin
This is a variation on the other answers, but I believe it's better to use a For/Each loop than a counter:
Dim rs As DAO.Recordset
Dim fld As DAO.Field
Set rs = CurrentDB.OpenRecordset("SELECT * FROM tblMain")
For Each fld In rs.Fields
Debug.Print fld.Name
Next fld
Set fld = Nothing
rs.Close
Set rs = Nothing
You can iterate through the fields collection of the recordset.
Code is OTTOMH
Dim NumFields as Integer
For NumFields = 0 to rs.Fields.Count -1
Debug.Print Rs.Fields(NumFields).Name
Next
Alternately, you can set a breakpoint at set rs = currentdb.openrecordset(sql) and then as soon as the statement executes, right-click on rs, choose add watch and view the whole thing in the Watches window.
Here is a script that will look for a field containing the string you specify in every table in an Access database (except System and Attached Tables) and write it to text files:
Option Compare Database
Option Explicit
Sub main()
Dim db As Database
Dim rs As Recordset
Dim bFinished As Boolean
Dim sFieldName As String
Dim iPosition, z, x As Integer
Dim bRetVal As Boolean
Dim tdTemp As TableDef
Dim iDatabaseNumbers As Integer
Const FIELD_TO_FIND = "FieldName"
Set db = CurrentDb
Open Left(db.Name, Len(db.Name) - 4) & "_" & FIELD_TO_FIND & ".txt" For Output As #1
For x = 0 To db.TableDefs.Count - 1
Set tdTemp = db.TableDefs(x)
bRetVal = IIf(tdTemp.Attributes And dbSystemObject, False, True)
If bRetVal Then
bRetVal = IIf(tdTemp.Attributes And dbAttachedTable, False, True)
End If
If bRetVal Then
Set rs = db.OpenRecordset(db.TableDefs(x).Name)
If rs.RecordCount > 0 Then
For z = 0 To rs.Fields.Count - 1
sFieldName = rs.Fields(z).Name
If InStr(1, sFieldName, FIELD_TO_FIND, vbTextCompare) > 0 Then
Print #1, db.TableDefs(x).Name
Exit For
End If
Next z
End If
End If
Next x
Close #1
MsgBox "Done"
End Sub
You could adjust accordingly to make it do what you need.