Am trying to create an empty DAO recordset rs2 whose structure is similar to an existing recordset rs1 (which has more than 200 fields). But I am facing an error which does not happen when I use ADO recordset:
dim rs2 as recordset
With rs2.Fields
For Each fld In rs.Fields
.Append fld, adVariant
Next
End With
Error is wrong number of arguments.
ADO allows you to add a field to the Recordset.Fields collection under certain circumstances. That is why the ADO version of your code runs without error.
However, the DAO Fields.Append method can not be used with a Recordset. Instead you would have to add a field to the table, or field expression to the query, which is used as the recordset's data source.
I know that this solution is not good for performance, but you can give it a try anyway.
First, clone the recordset:
Set rs2 = rs.clone
And then, if you only need the structure, empty it:
Do until rs2.EOF
rs2.Delete
Loop
Related
I have an AccessDB app where I need to grab the ItemIDs for the current user-applied filter into a new table to use downstream. Using the subform datasheet .recordsetclone property I can see the desired recordset, .recordcount reports the correct number of records. Otherwise, the following does not produce the desired temp table and AccessVBA does not complain.
Dim db As DAO.Database
Dim rstItemIDs As DAO.Recordset
Dim strSQL as String
Set db = CurrentDb
set rstItemIDs = Forms!Mainform![Data subform].Form.RecordsetClone
msgbox rstItemIDs.recordcount 'reports the correct result
strSQL = "SELECT rstItemIDs.ItemID INTO tempTable FROM rstItemIDs;"
db.Execute strSQL
Is it possible to construct a SQL Select query against a dao.recordset?
Thanks for any pointers you can provide.
Access SQL will not accept either a DAO or ADODB Recordset as the data source for a query.
However, I'm puzzled that Access does not complain when you try. With every attempt I made to reproduce your sample code, I got error #3078, "The Microsoft Access database engine cannot find the input table or query 'rstItemIDs'. Make sure it exists and that its name is spelled correctly."
Even DoCmd.SetWarnings False did not suppress that error message.
If you're interested in alternatives, you could persist tempTable (instead of creating a new version each time), then delete its contents and move through rstItemIDs adding each value to the second recordset. Although that is a RBAR (row by agonizing row) method, it may not be too painful with a small recordset.
A set-based approach could be to create a query based on your form's .RecordSource and .Filter properties. For example, with my form's .RecordSource as SELECT * FROM foo and the current form .Filter as id>10, this would give me a SELECT which returns the form's filtered records:
Replace(Me.RecordSource, ";", "") & vbcrlf & "AND " & Me.Filter
I have an access 2003 front end database with a form that allows users to see a sorted and/or filtered view of some data. The data is displayed in a sub-form.
The base data (loaded when the form is opened) is retrieved into a disconnected ADODB.Recordset object (static client side cursor). The sub-form's Recordset property is set to the disconnected recordset and all records are displayed.
Applying just a sort (in code) to the recordset object and then setting the sub-form to use the sorted recordset displays the data with the correct sort applied. The filter property is set to adFilterNone for this to work. All records are displayed (correct).
Applying just a filter (in code) to the recordset object and then setting the sub-form to use the filtered recordset displays the data with the correct filter applied. The sort property is set to an empty string for this to work. All records matching the filter are displayed (correct).
When both the sort property AND the filter property are set on the recordset, and that recordset is then set to the sub-form's Recordset property, only the first 100 matching records are displayed (incorrect). They are displayed in sort order. The underlying recordset object shows the correct record count for the filtered records, they just don't all display on the form.
Does anyone know why this is happening and if there is a way to get around this apart from creating a recordset using a new SQL string each time?
Thanks in advance.
What you are seeing with filtering and sorting is a known limitation of ADO recordsets.
Take a look at the list of ADO Cons listed on this page. Notice the one on the bottom:
http://www.utteraccess.com/wiki/index.php/Choosing_between_DAO_and_ADO
I couldn't find any documentation on MS's Support site about this so I don't know if it's a bug or simply a limitation. I'm assuming it's the latter.
FYI, I think MS has basically forgotten about ADO (classic). The last release of MDAC (which is how you obtain ADO) was 5/10/2005.
As far as a work-around for this problem, you can try using this function. It returns a new, filtered and sorted recordset. Just keep a big, full recordset handy and use this function to get a new one every time you do a sort/filter. This does increase your overall resource usage, especially memory.
I have used this function but it hasn't been fully tested to make sure it's bullet proof in every way. You might quickly find some bug or limitation with it. I actually had a note that it needed some kind of work but my note was unclear, I didn't have time to test it now, and I did find that I'm using this function in my production code so I think it's working.
Public Function GetFilteredRecordset(ByRef rsSource As ADODb.Recordset, _
ByVal sWhere As String, _
Optional ByVal sOrderBy As String, _
Optional ByVal LockType As ADODb.LockTypeEnum = adLockUnspecified) As ADODb.Recordset
Dim sOriginalOrderBy As String
sOriginalOrderBy = rsSource.Sort
Dim F As ADODb.Field
For Each F In rsSource.Fields
'Debug.Print F.Name
Next F
rsSource.Filter = sWhere
If sOrderBy <> "" Then
If Left(LCase(sOrderBy), 8) = "order by" Then sOrderBy = Trim(Right(sOrderBy, Len(sOrderBy) - 8))
rsSource.Sort = sOrderBy
End If
Dim rsF As ADODb.Recordset
Dim objStream As ADODb.Stream
'Create a New ADO 2.5 Stream object
Set objStream = New ADODb.Stream
'Save the Recordset to the Stream object in XML format
rsSource.Save objStream, adPersistXML
'Create an exact copy of the saved Recordset from the Stream Object
Set rsF = New ADODb.Recordset
rsF.Open objStream, , , LockType
rsSource.Filter = ""
rsSource.Sort = sOriginalOrderBy
'Close and de-reference the Stream object
objStream.Close
Set objStream = Nothing
Set GetFilteredRecordset = rsF
End Function
Another strange limitation of filtering ADO recordsets is that your OR keyword must always be on the top level. That is also documented in the link I posted above although I'm not sure if the examples given are accurate.
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.
So lets say I have some code like below to pull data from another access file:
Sub ADO_Recordset_OpenTable()
Dim rs As ADODB.Recordset
Dim cn As ADODB.Connection
Dim MyPath As String
MyPath = CurrentProject.Path
Set cn = New ADODB.Connection
cn.Provider = "Microsoft Jet 4.0 OLE DB Provider"
cn.ConnectionString = "Data Source=C:\Users\Justin\Desktop\ExampleFile.mdb"
cn.Open
Set rs = New ADODB.Recordset
rs.Open "Schedule", cn, adOpenDynamic, adLockReadOnly, adCmdTable
' I would like to at this point build a table within the currentdb file
' with the data in the recordset. Either some kind of create table or
' SQL INSERT?? Just trying to learn how to work with the data set
So within the example are my comments. Basically would like to know how to create a table out of the data contained with the recordset. I guess creating a tabledef? But this is DAO right? and I couldn't really use both DAO and ADO together in a routine right?
Thanks
Justin
You can use both ADO and DAO for different objects within the same procedure.
You could create a DAO.TableDef and examine the recordset's Fields collection, creating new TableDef fields matching each rs.Fields(i).Name and rs.Fields(i).Type
Once you have created the table structure (TableDef), you can loop through the recordset rows to build and execute INSERT statements to store the row values in your new table.
But that seems like waaaay too much work to me. I like Raj's SELECT INTO suggestion better. However, since you already know the table name and path to your MDB, I would reach first for DoCmd.TransferDatabase, and leave ADO only for tasks DAO can't do at all or can't do as conveniently as ADO.
Finally, if your primary interest on this one is exploring possibilities, take a look at the recordset's Save method. You could save with adPersistXML, then import the saved XML as a new table in your current db. See Save Method (ADO)
I have done this the ugly way - parse the incoming ADO recordset, build the CREATE TABLE statement and execute it, and then RBAR through the ADO dataset to insert into the local table.
You can also create a passthrough query which you can then use to SELECT * INTO MyNewTable FROM MyPassThroughQuery
You could try ADOX.
I am working with an Access 2003 database that has a subroutine using DAO code. This code loops through the table definitions and refreshes the ODBC connection string. I would like to convert this to ADO so I do not have to reference the DAO object library. Here is the code ...
Public Sub RefreshODBCLinks(newConnectionString As String)
Dim db As DAO.Database
Dim tb As DAO.TableDef
Set db = CurrentDb
For Each tb In db.TableDefs
If Left(tb.Connect, 4) = "ODBC" Then
tb.Connect = newConnectionString
tb.RefreshLink
Debug.Print "Refreshed ODBC table " & tb.Name
End If
Next tb
Set db = Nothing
MsgBox "New connection string is " & newConnectionString, vbOKOnly, "ODBC Links refreshed"
End Sub
The part I am unsure of is how to loop through the tables and get/set their connection strings.
DAO is really best for that, you cannot refresh the link with ADO rather you would need to use ADOX (Some relevant code here).
You can acces the connection string via Jet OLEDB:Link Provider String
If avoiding a reference for DAO is your goal, you could just modify your existing procedure to use late binding for DAO. As an example, this sub should work without a reference set for DAO.
Public Sub DAO_without_reference()
Dim db As Object
Dim td As Object
Set db = CurrentDb
For Each td In db.TableDefs
Debug.Print td.Name
Next td
Set db = Nothing
End Sub
You would not have Intellisense to help you with DAO properties, methods, and constants while writing the code, but the code can still work with late binding.
I think this would be your easiest alternative if you are determined to avoid a DAO reference. However, I have never developed an Access project without a DAO reference, and I don't understand why you are opposed to adding it.
Edit: Also if you use late binding and any DAO constants, your code must use the constant value rather than the name.