Is there something wrong with the following code (in an ADP form) ?
Private Sub cmbSearchCode_AfterUpdate()
Me.Recordset.Find "usr_cde = '" & ctl & "'"`
ctl = null
end sub
It behaves erratically: the first times after opening the form, it works, then suddenly it does not seem to do anything anymore.
I replaced it by this code that seems to have no problems:
With Me.RecordsetClone
.Find "usr_cde = '" & ctl & "'"
Me.Bookmark = .Bookmark
End With
ctl = Null
Any explanation ?
The client is A2003, the server is SS 2000 (I know it's old, nothing I can do about it !)
The Form.Recordset Property is a fairly new addition to access, there are a couple of bits in the helptexts, it's how they combine that I believe is causing your issue.
If a form is based on a query, for example, referring to the
Recordset property is the equivalent of cloning a Recordset object by
using the same query. However, unlike using the RecordsetClone
property, changing which record is current in the recordset returned
by the form's Recordset property also sets the current record of the
form.
So while it doesn't look like it at first, it does actually clone the recordset and create a new copy of it. Just it keeps it synchronised.
So you have a new Recordset Object and here's the rub:
A new Recordset object is automatically added to the Recordsets
collection when you open the object, and is automatically removed when
you close it.
First time round, you clone the recordset find on usr_cde, and the form magically sets the current record to match.
Second time round, you clone the recordset find on usr_cde, but the magical record synchronisation is still stuck on the first copy of the recordset that has been persisted in the Recordsets collection.
So you just need to close the recordset, but to make sure you don't just create another copy and close that do the following:
'untested
Private Sub cmbSearchCode_AfterUpdate()
Dim rs as adodb.Recordset
Set rs = Me.Recordset
rs.Find "usr_cde = '" & ctl & "'"`
rs.Close
ctl = null
end sub
I haven't had a chance to test this, but you already have a working solution using .Bookmark. I hope this explains the unexpected behaviour.
Related
I have a form, myForm, that includes a subform, mySubform. The records in mySubform have a many to one relationship with the record source of the myForm, as expected, and There is a combobox in mySubform, myCombo, whose values is linked to one of the columns of the record source of mySubform.
I have been having difficulty to delete a record in mySubform, by erasing the current value in myCombo. I have put the code below under the OnChange event of myCombo (after trying many different alternatives).
Please not that I have simplified the delete query and the actual one works fine in the back-end (SQL Server). However, after performing the delete operation, I get this error when the execution reaches Me.Requery:
Error 3162 You tried to assign a Null value to a variable that is not a Variant data type.
Also, after having the degugger skip the Me.Requery and going back to the form, the deleted record shows "#DELETED" in every combobox and textbox for that particular record, and mySubform is not any good beyond that point.
I have looked up very many resources and tried different statements instead of Me.Requery, but none has worked so far. Help is much appreciated.
Private Sub myCombo_Change()
If IsNull(Me.myCombo.Text) Or Me.myCombo.Text = vbNullString Then
Dim strSql As String
strSql = "DELETE FROM mySubformRecordSource WHERE PrimaryKeyColumn= " & theCurrentPrimaryKeyValueForTheValueIn_MyCombo
CurrentDb.Execute strSql
Me.Requery
End If
End Sub
I solved the problem by putting Me.Dirty = False after reading this discussion:
Editing Record issues in Access / SQL (Write Conflict)
Private Sub myCombo_Change()
If IsNull(Me.myCombo.Text) Or Me.myCombo.Text = vbNullString Then
Me.Dirty = False
Dim strSql As String
strSql = "DELETE FROM mySubformRecordSource WHERE PrimaryKeyColumn= " & theCurrentPrimaryKeyValueForTheValueIn_MyCombo
CurrentDb.Execute strSql
Me.Requery
End If
End Sub
The form and subform work flawlessly now, but I still dont quite understand the complete logic behind the solution. It has something to do with Me.Dirty=False saving the changes to the content of a non-nullable (or nullable, both work fine) column before programmatically delete the entire record. How exactly though, I dont know and will appreciate input.
Not entering on the choices of used cursors etc I think that the solution could be simply "requery" the parent form called by the code of the subform.
But i also have found similar situation in past projects that i made (sorry not beeing exaustive, i'm not using VBA anymore) and the requery command was done on subform but from the main form code, something like this:
If local_procedures.deleteContratto(anagrafica_id_par, servizio_id_par) = OK Then
Me.subfrm_contratti_frm_anagrafica.Requery
...
In this case you could delegate parent form to call delete procedure and update logic (.Requery) of the child form, by mean of an event raised from subForm listen on parent for example.
For the sake of completeness i started hinting about cursors because i think it's the underlying reason for these kind of sync failures between recordsets, the parent form one and the child form one.
There are many advices about using dbSeeChanges with db.Execute on SQL Server backend and the resulting default cursors.
UPDATE
Here's a code snippet where a custom event is raised from child to be caught from parent that is delegated to execute code regarding the child.
This pattern doesn't break the parent-child/one-to-many logic, assuming parent has more decision power than child.
' in mySubform
' assuming theCurrentPrimaryKeyValueForTheValueIn_MyCombo is a Long ID
Public Event DeleteRequest(record_ID As Long)
Private Sub myCombo_Change()
If IsNull(Me.myCombo.Text) Or Me.myCombo.Text = vbNullString Then
RaiseEvent DeleteRequest(theCurrentPrimaryKeyValueForTheValueIn_MyCombo)
End If
End Sub
' in myForm
Private WithEvents frmMySubform As Form__mySubform
' you could have more instances of mySubform concerning different issues or subrecords and manage each one correctly ...
Private WithEvents frmMySubform2nd As Form__mySubform
Private Sub frmMySubform_DeleteRequest(record_ID As Long)
Dim strSql As String
strSql = "DELETE FROM mySubformRecordSource WHERE PrimaryKeyColumn= " & record_ID
CurrentDb.Execute strSql
With Me.frmMySubform.Form
.Requery
' ...
end With
End Sub
Private Sub frmMySubform2nd_DeleteRequest(record_ID As Long)
Dim strSql As String
strSql = "DELETE FROM my2ndSubformRecordSource WHERE PrimaryKeyColumn= " & record_ID
CurrentDb.Execute strSql
With Me.frmMySubform2nd.Form
.Requery
' ...
end With
End Sub
DealID is set using a Combobox and it needs to Display up to 3 MID's in the field below it
Private Sub DealID_AfterUpdate()
Dim strFilter As String
strFilter = "DealID = " & Me!DealID
Me!MID = DLookup("MID", "DealContent", strFilter)
Exit_ProductID_AfterUpdate:
Exit Sub
End Sub
That's the code I'm using the obvious limitation is Dlookup only returns the first result it finds.
That creates 2 problems but Ill focus on the first, It won't display more than one MID
so how I can get the 1-3 MIDs to be displayed?
The second issue I have is more in-depth but if anyone wants to help, a personal chat would be appreciated. Basically The form above is a child form and I need it to save a separate entry in the forms table for each Mount ID.
If anyone would like to help but doesn't understand(as is often the case with my submissions), I think screen sharing on Skype is the best option.
As already said in a comment, you can't use DLookup to return more than one value.
You need to select the first three MIDs into a Recordset, loop through them and append them to Me!MID:
Dim RS As DAO.Recordset
Dim SQL As String
'ordering is only important if you want the FIRST three MIDs.
'If you don't care, just omit the "order by MID" part.
SQL = "select top 3 MID from DealContent where DealID = xxx order by MID"
Set RS = CurrentDb.OpenRecordset(SQL)
Do While Not RS.EOF
Me!MID = Me!MID & RS("mid") & " "
RS.MoveNext
Loop
RS.Close
Set RS = Nothing
Note that this example uses DAO, which is the default (and recommended by MS) data access technology in newer Access versions. Older versions used ADO as default.
DAO works there as well, you just need a reference to Microsoft DAO x.x Object Library.
EDIT: Changed as I have a different issue with the same code
2nd Edit: Adding additional code that seems to be casuing the issue
I have created a vba program in access that aggregates data from a number of external sources and write the results into a new table. Ideally when I run the program I want to wipe out all of the data that is currently in the table and replace it with my new data. I am currently currently deleting all of the data in the table... then writing my new data
Here is the code for reference
Function getTestFixtures(FixtureName As String) As Recordset
Dim db As Database
Set db = OpenDatabase(GetDBPath & "TestFixtures.xlsx", False, False, "Excel 12.0;HDR=Yes;")
If db Is Nothing Then
MsgBox "Can't find the file!", vbExclamation, ThisWorkbook.Name
Exit Function
End If
Set getTestFixtures = db.OpenRecordset("Select * from [" & FixtureName & "$]")
End Function
The recordset created above is modified and the output data is placed in a dictionary and passed to this function.
Sub Write_OTDC_Data(POlist As Dictionary)
Dim Rst As Recordset
DoCmd.SetWarnings False
DoCmd.runsql "Delete * from [OTDC Results]"
DoCmd.SetWarnings True
Set Rst = CurrentDb.OpenRecordset("OTDC Results")
With Rst
For Each key In POlist.Keys
.AddNew
For i = 0 To 9
.Fields(i).value = POlist(key)(i)
Next
.Update
Next
.Close
End With
End Sub
My Problem is that I get the following error if I try to change anything after running both of the above procuedures.
Running either in isolation does not generate the error.
I'm unsure whether this question is still unresolved. In case it's not, I have some suggestions for you to try, but not a lot of confidence they will cure the problem.
Try DoCmd.TransferSpreadsheet to import the sheet's data into a scratch table instead of using OpenDatabase with the workbook.
In your MsgBox, I wonder whether ThisWorkbook.Name means anything to an Access application. Aside from that, I would check whether the workbook exists, then open it (or import the sheet from it) only if the file is found.
If Len(Dir(GetDBPath & "TestFixtures.xlsx")) = 0 Then
'not found
Else
'use it
End If
Actually I'm unclear why you don't get an error from OpenDatabase if the workbook file doesn't exist. And that makes me suspicious of DoCmd.SetWarnings False Never, ever turn SetWarnings off. Doing so suppresses important information. And it is completely unnecessary. Set a DAO.Database object variable to CurrentDB(), then use this instead:
dbObjectVariable.Execute "Delete from [OTDC Results]", dbFailOnError
Add an error handler to deal with any problems dbFailOnError exposes.
Finally, this bears repeating because it's so important. NEVER turn SetWarnings off.
I am really new to programming access. I converted an old 97 access db to access 2007 and it has been working fine for awhile. Today I go into it and was snooping around in design view and looking at the code. I didn't change anything. Just looked. When I went to run the code I kept getting the "Not a valid bookmark" error. Just to be sure I opened up the old program I converted and all the code is the same the line that is giving me the problem is the
Me.Bookmark = pos
The following is the whole routine.
Thanks in advance.
Private Sub ProductID_AfterUpdate()
Dim pos As Variant
Me![UnitPrice] = Me![ProductID].Column(2)
Me![ProductName] = Me![ProductID].Column(1)
Me![GLAcct] = Me![ProductID].Column(3)
DoCmd.DoMenuItem acFormBar, acRecordsMenu, acSaveRecord, , acMenuVer70
pos = Me.Bookmark
Me.Requery
Me.Bookmark = pos
End Sub
Edit: I already tried compact and repair.
You will need to change this code as it is out of date for quite some time. I am not sure why it worked in the first place, because the bookmarks will have changed after updating, saving and requerying. I have commented the requery line, because it does not seem to serve a useful purpose, if you wish to update the combo, requery that. If you wish to find a record after an action, save the unique ID to a variable and find it. DoMenuItem is deprecated, you can use RunCommand instead, in this case, however, Me.Dirty=false will save.
Private Sub ProductID_AfterUpdate()
Dim pos As Variant
Me![UnitPrice] = Me![ProductID].Column(2)
Me![ProductName] = Me![ProductID].Column(1)
Me![GLAcct] = Me![ProductID].Column(3)
Me.Dirty=False
'pos = Me.Bookmark
'Me.Requery
'Me.Bookmark = pos
End Sub
To requery a form and return to the same record, do this:
Dim lngPKValue As Long
With Me.RecordsetClone
lngPKValue = Me!ID
' Me.Dirty = False is unnecessary, as the Requery saves the data
Me.Requery
.FindFirst "[ID]=" & lngPKValue
If Not .NoMatch Then
Me.Bookmark = .Bookmark
End If
End With
Now, where you put this code depents. I don't quite see in the original code why there's a need to requery, as the updates have been done to the record you're already on, so you're not really accomplishing anything by requerying and returning to the record you started on. But there are circumstances in which you'd want to do that (e.g., in an ordered recordset where you've edited values that will change the place of this particular record in the sorted result), and the above is the way to do it.
If it's necessary, you'd probably want to turn off painting of the form (Me.Painting = False, the Me.Painting = True once you've set the bookmark) or of the application (Application.Echo = False/True) so the screen doesn't flicker and you don't see the requerying and navigating. But be sure you add an error handler, since if an error happens while screen painting is turned off, your user may end up stuck and unable to continue.
HI,
I am trying to write a query in vba and to save its result in a report.
I am a beginner. this is what i have tried
can somebody correct me
Dim cn As New ADODB.Connection, rs As New ADODB.Recordset
Dim sql As String
Set cn = CurrentProject.Connection
sql = "Select * from table1 where empno is 0"
rs.Open sql, cn
While Not rs.EOF
' here i think i should save the result in a report but i am not sure how
rs.MoveNext
Wend
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
Also how do i change this query to run this on all tables in a database
IF you are wanting to create a report using MS Access's report generator, you will have to use a Query Object (there might be a way to trick MS Access into running it off of your record set, but it's probably not worth your effort).
You can create the Query Object on the "Database" window. Click the Query button in the objects list, and then click on New. In the resulting editor you can create the query graphically or if you prefer with SQL. Save the query and give it a meaning full name.
Similarly the report can be created on the "Database" window. Click on the Report button and then on New. In the resulting wizard, you'll link the report to the query you just created.
Update: As D.W. Fenton said, you can embed the query right within the Report Object without creating a separate Query Object. My preference is to create one anyway.
The problem with this method is you would have to create a separate query and report for each table.
IF you just want to dump the result out to a text file (to read/print later), then you can do it using recordsets like you are in your VBA code. It will look something like this
'...
dim strFoo as string
dim strBar as string
'...
if not rs.bof then
rd.MoveFirst
end if
While Not rs.EOF
strFoo = rs("foo") 'copy the value in the field
'named "foo" into strFoo.
strBar = rs("bar")
'... etc. for all fields you want
'
'write out the values to a text file
'(I'll leave this an exercise for the reader)
'
rs.MoveNext
Wend
'...
Parsing all of the tables can be done in a loop something like this:
dim strTableName as string
dim db As Database
'...
Set db = CurrentDb
db.TableDefs.Refresh
For Each myTable In db.TableDefs
If Len(myTable.Connect) > 0 Then
strTableName = myTable.Name
'...
'Do something with the table
'...
End If
Next
set db = nothing
=======================UPDATE=======================
It is possible to run an MS-Access Report from a record set. To repease what I said to tksy's question
From Access Web you can use the "name" property of a recordset. You resulting code would look something like this:
In the report
Private Sub Report_Open(Cancel As Integer)
Me.RecordSource = gMyRecordSet.Name
End Sub
In the calling object (module, form, etc.)
Public gMyRecordSet As Recordset
'...
Public Sub callMyReport()
'...
Set gMyRecordSet = CurrentDb.OpenRecordset("Select * " & _
"from foo " & _
"where bar='yaddah'")
DoCmd.OpenReport "myReport", acViewPreview
'...
gMyRecordSet.Close
Set gMyRecordSet = Nothing
'...
End Sub
Q.E.D.
Normally you would design the report based on a data source. Then after your report is done and working properly you use VBA to display or save the report.
To run this for each table in the database, I'd suggest writing a function that looped through CurrentData.AllTables(i) and then called your function above in each iteration
Hope this helps
If you want to simply view the results, you can create a query. For example, here is some rough, mostly untested VBA:
Sub ViewMySQL
Dim strSQL as String
Dim strName As String
'Note that this is not sensible in that you
'will end up with as many queries open as there are tables
For Each tdf In CurrentDB.TableDefs
If Left(tdf.Name,4)<>"Msys" Then
strName = "tmp" & tdf.Name
strSQL = "Select * from [" & tdf.Name & "] where empno = 0"
UpdateQuery strName, strSQL
DoCmd.OpenQuery strName, acViewNormal
End If
Next
End Sub
Function UpdateQuery(QueryName, SQL)
If IsNull(DLookup("Name", "MsysObjects", "Name='" & QueryName & "'")) Then
CurrentDb.CreateQueryDef QueryName, SQL
Else
CurrentDb.QueryDefs(QueryName).SQL = SQL
End If
UpdateQuery = True
End Function
You may also be interested in MDB Doc, an add-in for Microsoft Access 97-2003 that allows you to document objects/properties within an Access database to an HTML file.
-- http://mdbdoc.sourceforge.net/
It's not entirely clear to me what you want to do. If you want to view the results of SQL statement, you'd create a form and set its recordsource to "Select * from table1 where empno is 0". Then you could view the results one record at a time.
If that's not what you want, then I'm afraid I just don't have enough information to answer your question.
From what you have said so far, I don't see any reason why you need VBA or a report, since you just want to view the data. A report is for printing, a form is for viewing and editing. A report is page-oriented and not that easy to navigate, while a form is record-oriented, and allows you to edit the data (if you want to).
More information about what you want to accomplish will help us give you better answers.
Had the same question. just use the clipboard!
select the query results by click/dragging over all field names shown
press ctrl-c to copy to windows clipboard
open a blank document in word and click inside it
press ctrl-v to paste from clipboard.