Use VBA to determine if a table can be edited - ms-access

I have the following procedure in Form_Load:
Dim accObject As Access.AccessObject
For Each accObject In CurrentData.AllTables
Me.cboSelectTable.AddItem accObject.Name
Next
This populates the combo box with ALL the tables including system tables and those linked to SQL Server. The problem is, I am only hoping to get back tables which can be edited. How can this be determined using VBA?

When retrieving your set of target table names you can ignore system tables (Like "MSys*") and temporary tables (Like "~*"). And ignore linked tables by checking whether the Connect property is anything other than a zero-length string.
Dim tdf As DAO.TableDef
For Each tdf In CurrentDb.TableDefs
With tdf
If Not (.Name Like "~*" Or .Name Like "MSys*") Then
If Len(.Connect) = 0 Then
Me.cboSelectTable.AddItem .Name
End If
End If
End With
Next
Set tdf = Nothing
If that doesn't give you everything you want, you can also check whether tdf.Updatable is True for each table.

Related

MS Access delete then append tabledef breaks querydef

Following this question thread, was able to successfully code the change suggested by "changing the sourcetable of a linked table in access 2007 with C#". However, it appears this customer has queries coded with relationships defined at the query level and the delete/append process breaks the relationships. Anyone have any idea how to preserve the relationships? And why is it that the tabledef.Sourcetable can't be updated?
Code snip:
Option Compare Database
Sub test()
Dim tdf As TableDef
Dim db As Database
Set db = CurrentDb
Open "out.txt" For Output As #1
For Each tdf In db.TableDefs
If tdf.Connect <> vbNullString Then
Print #1, tdf.Name; " -- "; tdf.SourceTableName; " -- "; tdf.Connect
Select Case tdf.SourceTableName
Case "CTITLU.txt"
'tdf.SourceTableName = "dbo.GRANTSADJS"
'tdf.Connect = "ODBC;DRIVER=SQL Server;SERVER=DCFTDBCL01-L01\EDS_DEV;DATABASE=GRANTSDB2;UID=grants_reader;PWD=xxxxx;TABLE=DBO.GRANTSTABL"
tdf.RefreshLink
End Select
End If
Next
End Sub
When I run this with just the tdf.connect syntax uncommented, it errors on the tdf.refreshlink call with "Run-time error '3011': The Microsoft Access database engine could not find the object 'objectname'..." I'm trying to update a text linked table to the equivalent SQL Server based linked table. The objectname does have spaces and hyphens in it, but it is correctly showing the name in the error message. For whatever reason, the previous developer shipped dumps of the tables to a file system instead of linking the tables directly. This is a small DB with very light transactional activity so there is very little chance this will cause any issues. When the tdf.SourceTableName is uncommented, throws the "Run-time error '3268': Cannot set this property once the object is part of a collection."
I followed other threads indicating this issue (noted above), and was successful using the tdf.delete / tdf.append calls to duplicate the tabledef with new source tablename and connection info. However, the dependent query's relationship definitions have disappeared and the query is unusable without redefining all of the links.
C Perkins, thanks, that was it. There was a slight difference in the table definition such that when using delete/append, it 'broke' the relationships (yes joins) in the related query. Using a DB view to fix that, it worked just fine. However it still 'moves' the query from its former place as being related to the original table object. Our customers will at least have their current data and not a weekly snapshot. Thanks again.

Create a table from a Form MS Access

I have a continuous form in MS Access that I would like to create a table from. Most of the fields are populated by a query, but some fields will be user entered and there will also be calculated fields. Basically I need to create a snapshot of the data when a users clicks a button at any given point and then save the table with the value of one of the fields and that particular date. What is the best way to achieve this?
Thank you!
-Charlie
What you actually want to do is learn how to use recordset clones. You should also reuse a table seeing in how there is a pretty good chance you'll want fresh data on each pull.
Because this isnt a code writing service, ill let you figure out the details, but this should be plenty to get you started.
Sub Print_Field_Names()
Dim rst As Recordset, intI As Integer
Dim fld As Field
dim vbSql as string
vbSql = "DELETE * FROM Tbl"
DoCmd.RunSQL vbSql
Set rst = Me.RecordsetClone
For Each fld in rst.Fields
' Print field names.
Debug.Print fld.Name
Next
End Sub
In the loop through the original recordset clone, you should try for yourself how to edit another recordset on your reusuable table to "Fill" the new table with your form data. this is actually really simple and the masochist in me wants you to actually suffer since you didnt try anything on your own first :P

VBA editing bookmark in a building block [duplicate]

I want to create a template in Word, that I can then use Access to merge data into. The Access data has various levels of grouping. Within each grouping, there are subqueries that may also have grouping. Because there is grouping, certain pages will need to be repeated.
Example: I have a query that prints the details of a classroom. There are many students in each classroom, as well. So, I would like to have a DOTM template that groups each student by class. Then prints the first class details, then the students, then moving on the next class, then students, etc.
The Access tables/queries, etc. are not an issue. It's creating a template, then merging it, that I'm having issues with. Right now, I just have a simple template (Dotm file). The file has some boilerplate stuff, and some bookmarks. I then use this code to interact with the dotm file:
Dim objWord As Word.Application
Dim PauseTime, Start, Timer As Integer
Dim wrkCurrent As DAO.Workspace
Set objWord = CreateObject("Word.Application")
objWord.Visible = False 'True is visible
Dim sql As String
sql = "SELECT * FROM tbl_School" 'ex query that produces more that one record
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Set wrkCurrent = DBEngine.Workspaces(0)
Set dbs = CurrentDb
Set rst = dbs.OpenRecordset(sql)
objWord.Documents.Add ("C:\test\Test.dotm")
'this template has one page, with one bookmark, School_Name. What I want
' to happen is that, for every record, create a new page, with this
'bookmark filled in.
If (Not rst.EOF) Then
With rst
Do Until .EOF
objWord.ActiveDocument.Bookmarks("School_Name").Select
objWord.Selection.Text = rst!school_name
.MoveNext
Loop
End With
End If
objWord.ActiveDocument.SaveAs ("C:\test\MyNewDocument.docx")
objWord.PrintOut
objWord.Quit
Set objWord = Nothing
The problem is that this only prints the first one, then errors. How would I do the grouping? FYI, I know I can do this in reports, but I must allow the report to be exported to Word, retaining images, graphics, etc. which the export loses.

Converting from Access to SQL Back End (Multi-Value Fields)

So I'm converting an access back-end to SQL. I've tried a few different tools (SSMA, Upsizing Wizard, and a simple import). I've found so far that the SSMA tool and importing seem to work the best, eliminating most of the work necessary for me. However, I'm running into one issue I can't figure out how to overcome.
Two fields allow multiple values (dropdown with check boxes). In converting these, it errors in a way that it not only doesn't carry all of the information over, but also grabs information from another field (and doesn't carry that information over).
I've tried forcing access to only accept the first value (and get rid of multi-values all together), but it won't let me.
Any ideas?
This should get you started. It will turn all those values which are selected in the multi select field into their own table. You will need to establish the relationships between the three tables to create a true many to many relationship after the fact.
Sub ExtractMultiValueFields()
Dim JoinTable As New DAO.TableDef
JoinTable.Name = "JoinTable"
With JoinTable
.Fields.Append .CreateField("MainTableId", dbInteger)
.Fields.Append .CreateField("JoinToValue", dbText)
End With
Dim joinRs As DAO.Recordset
CurrentDb.TableDefs.Append JoinTable
Set joinRs = CurrentDb.OpenRecordset("JoinTable")
Dim rs As DAO.Recordset
Dim childrs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("select * from table1")
Do While Not rs.EOF
Debug.Print rs("ID")
Set childrs = rs("col1").Value
Do While Not childrs.EOF
Debug.Print childrs("value") 'always "value"
joinRs.AddNew
joinRs("MainTableId") = rs("ID")
joinRs("JoinToValue") = childrs("value")
joinRs.Update
childrs.MoveNext
Loop
rs.MoveNext
Loop
End Sub

DAO.Recordset.Update results in reckord lock

I am trying to run the following code to loop around a recordset and do updates where neccessary.
I have a Microsoft Access database connected to a MySql backend. Whenever I run this code I get the following error:
3197 error: The Microsoft Office Access database engine stopped the process because you and another user are attempting to change the same data at the same time.
The code is below:
Private Sub test()
Dim rs As DAO.Recordset, rsCnt As Long, i As Long
Set rs = CurrentDb.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.MoveLast
rsCnt = rs.RecordCount
rs.MoveFirst
For i = 1 To rsCnt
rs.Edit
rs!MyFieldInTable = "test"
rs.Update
Next i
End Sub
I thought the Access database might be corrupt so I pulled an earlier backup but it's doing the same thing which makes me think it's a MySql issue.
We use an identical piece of code on another version of this database linked to a different MySql table and it works fine.
Also, when I open the query the record-set is based on I can edit the data in the query without any issues.
Just to add, on the first loop, rs!MyFieldInTable is updated, then I get the error.
It does not appear that you are moving to another record in the recordset. Simply incrementing i doesn't move to the next record. A more traditional approach would be to iterate over the recordset without the need for your other variables (i and rsCnt).
Dim rs as DAO.Recordset
Set rs = CurrentDb.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.moveFirst
Do Until rs.EOF
rs.Edit
rs!FieldNameHere = "test"
rs.Update
rs.MoveNext
Loop
EDIT
After a bit of searching I came across this thread which seems to be similar to your issue. At the bottom of the thread a suggestion is made to modify the ODBC settings for your MySQL DSN by selecting the "Advanced" tab and selecting the option to "Return Matching Rows". The post also says to drop the linked table and then re-link it to your Access database.
I haven't used Access with MySQL in the past, so I have no idea whether this will work or not, so proceed with caution!
You may also try changing your recordset to use the dbOptimistic flag for the recordset locking option to see if that helps at all:
set rs = CurrentDB.OpenRecordSet("qryMyQuery", DB_OPEN_DYNASET, dbOptimistic)
Two things you can try. First, try adding the dbSeeChanges option when opening the recordset:
Dim rs as DAO.Recordset, db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryMyQuery", dbOpenDynaset, dbSeeChanges)
Do Until rs.EOF
rs.Edit
rs!FieldNameHere = "test"
rs.Update
rs.MoveNext
Loop
The other option, as #HansUp suggested, is to use a SQL update statement instead of a dynamic recordset. The key there is to open the recordset as a snapshot, so that changes you make to the records do not affect the recordset itself.
Dim rs as DAO.Recordset, db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryBatchPayments", dbOpenSnapshot)
Do Until rs.EOF
db.Execute "UPDATE Payments " & _
"SET DCReference='test' " & _
"WHERE PaymentID=" & !PaymentID, dbFailOnError
rs.MoveNext
Loop
I was having the same problem and my solution turned out to be the default value for BIT(1) fields. Access does not like these to be null. Make sure you use either 0 or 1 in mysql for these fields.
I don't have MySQL here to try this against, but it looks to me as if your code is not advancing the recordset after the rs.Update method is executed, so that you are trying to udate the same field in the fierst record.
Add this line after the rs.Update:
rs.MoveNext
Hope that helps.
Try calling OpenRecordset from an object variable set to CurrentDb(), rather than directly from CurrentDb().
Dim rs as DAO.Recordset
Dim db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.moveFirst
Do Until rs.EOF
rs.Edit
rs!FieldNameHere = "test"
rs.Update
rs.MoveNext
Loop
The reason for that suggestion is I've found operations on CurrentDb directly can throw an error about "block not set". But I don't get the error when using an object variable instead. And ISTR OpenRecordset was one such operation where this was an issue.
Also, my impression was your approach is a cumbersome way to accomplish the equivalent of:
UPDATE qryMyQuery SET FieldNameHere = "test";
However, I suspect the example is a proxy for a real world situation where the recordset approach is useful. Still that makes me wonder whether you would see the same or a different error when executing the UPDATE statement.
If you continue to have trouble with this, it may help to show us the SQL View for qryMyQuery.
I have discovered that if one tries to save data which are the same as the one already in the MySql record Access will display this kind of error. I've tried some suggestions from this thread but did not help.
The simple solution for this is to save a slightly diffrent data by using a manual time-stamp. Here is an example of heaving a sort order field and setting it to 10, 20, 30...
i = 10
timeStamp = Now()
Do Until Employee.EOF
Employee.Edit
Employee!SortOrderDefault = i
Employee!LastUpdated = timeStamp
Employee.Update
i = i + 10
Employee.MoveNext
Loop
I've tried automatic time-stamp in the MySql table but did not help when the new entry data is the same as the old one.
My little helpful hint is, bits are very, very, very bad data types to use when linking SQL tables to Microsoft Access because only SQL Server understands what a bit is, Microsoft Access has a hard time interpreting what a bit is. Change any bit datatypes to int (integers) and relink your tables that should clear things up. Also, make sure your Booleans always contain a 1 or a 0 (not a yes/no or a true/flase) in your VBA code or your updates will fail to the linked SQL tables because Microsoft Access will try to update them with a True/False or a Yes/No and SQL will not like that.
I also had same problem; i solved them adding those to code using dao.recordset:
**rst.lockedits = true**
rst.edit
rst.fields(...).value = 1 / rst!... = 1
rst.update
**rst.lockedits = false**
this seems fix conflict between just opened data (such as in a form) and updating them with code.
Sorry for my bad english... i read a lot but i never had learn it! I'm just italian.