Runtime error 3164 for Access when copying [duplicate] - sql

I am having a difficult time how to properly copy specific field data from previous records on my user form. I don't have a code sample to show but my request is very simplistic.
Currently, out of 12 fields, I have 6 that I often repeat data. I can click on and press Ctrl+' ("Insert the value from the same field in the previous record") and it performs the task I want. However, it adds a lot of time to the task. I simply want to write VBA code to perform that command to those specific fields.
I haven't been able to get SendKeys to work. DLast appears to provide random data at times. I feel like this should be a very simple request but for some reason I am not finding a functional solution for it.

Don't fiddle with arrays or queries - use the power of DAO:
Private Sub CopyButton_Click()
CopyRecord
End Sub
If a record is selected, copy this.
If a new record is selected, copy the last (previous) record.
Private Sub CopyRecord()
Dim Source As DAO.Recordset
Dim Insert As DAO.Recordset
Dim Field As DAO.Field
' Live recordset.
Set Insert = Me.RecordsetClone
' Source recordset.
Set Source = Insert.Clone
If Me.NewRecord Then
' Copy the last record.
Source.MoveLast
Else
' Copy the current record.
Source.Bookmark = Me.Bookmark
End If
Insert.AddNew
For Each Field In Source.Fields
With Field
If .Attributes And dbAutoIncrField Then
' Skip Autonumber or GUID field.
Else
Select Case .Name
' List names of fields to copy.
Case "FirstField", "AnotherField", "YetAField" ' etc.
' Copy field content.
Insert.Fields(.Name).Value = Source.Fields(.Name).Value
End Select
End If
End With
Next
Insert.Update
Insert.Close
Source.Close
End Sub
This also, by the way, is an excellent example of the difference between the RecordsetClone and the Clone of a recordset - the first being "the records of the form", while the second is an independant copy.
This also means, that the form will update automatically and immediately.

Provided that it's a simple form to edit a simple table, and that the bound data field names match the control names, you may get away with
If Me.Recordset.AbsolutePosition > 0 Then
With Me.Recordset.Clone()
.AbsolutePosition = Me.Recordset.AbsolutePosition - 1
Dim control_name As Variant
For Each control_name In Array("field1", "field2", "field3", "field4", "field5", "field6")
Me.Controls(control_name).Value = .Fields(control_name).Value
Next
End With
End If
which you assign to a separate button on the same form.

You have a good idea post here already.
You could also say place a function in the before insert event. This event ONLY fires when you start typing into a NEW reocrd, and it becomes dirty.
So, maybe this:
Private Sub Form_BeforeInsert(Cancel As Integer)
Dim rstPrevious As DAO.Recordset
Dim strSQL As String
strSQL = "SELECT TOP 1 * FROM tblPeople ORDER BY ID DESC"
Set rstPrevious = CurrentDb.OpenRecordset(strSQL)
' auto file out some previous values
If rstPrevious.RecordCount > 0 Then
Me.Firstname = rstPrevious!Firstname
Me.LastName = rstPrevious!LastName
End If
End Sub
And some good ideas in say having a "list" or "array" of controls/fields to setup, so you don't have to write a lot of code. (as suggested in the other post/answer here)

Related

MS Access VBA equivalent to Ctrl+'?

I am having a difficult time how to properly copy specific field data from previous records on my user form. I don't have a code sample to show but my request is very simplistic.
Currently, out of 12 fields, I have 6 that I often repeat data. I can click on and press Ctrl+' ("Insert the value from the same field in the previous record") and it performs the task I want. However, it adds a lot of time to the task. I simply want to write VBA code to perform that command to those specific fields.
I haven't been able to get SendKeys to work. DLast appears to provide random data at times. I feel like this should be a very simple request but for some reason I am not finding a functional solution for it.
Don't fiddle with arrays or queries - use the power of DAO:
Private Sub CopyButton_Click()
CopyRecord
End Sub
If a record is selected, copy this.
If a new record is selected, copy the last (previous) record.
Private Sub CopyRecord()
Dim Source As DAO.Recordset
Dim Insert As DAO.Recordset
Dim Field As DAO.Field
' Live recordset.
Set Insert = Me.RecordsetClone
' Source recordset.
Set Source = Insert.Clone
If Me.NewRecord Then
' Copy the last record.
Source.MoveLast
Else
' Copy the current record.
Source.Bookmark = Me.Bookmark
End If
Insert.AddNew
For Each Field In Source.Fields
With Field
If .Attributes And dbAutoIncrField Then
' Skip Autonumber or GUID field.
Else
Select Case .Name
' List names of fields to copy.
Case "FirstField", "AnotherField", "YetAField" ' etc.
' Copy field content.
Insert.Fields(.Name).Value = Source.Fields(.Name).Value
End Select
End If
End With
Next
Insert.Update
Insert.Close
Source.Close
End Sub
This also, by the way, is an excellent example of the difference between the RecordsetClone and the Clone of a recordset - the first being "the records of the form", while the second is an independant copy.
This also means, that the form will update automatically and immediately.
Provided that it's a simple form to edit a simple table, and that the bound data field names match the control names, you may get away with
If Me.Recordset.AbsolutePosition > 0 Then
With Me.Recordset.Clone()
.AbsolutePosition = Me.Recordset.AbsolutePosition - 1
Dim control_name As Variant
For Each control_name In Array("field1", "field2", "field3", "field4", "field5", "field6")
Me.Controls(control_name).Value = .Fields(control_name).Value
Next
End With
End If
which you assign to a separate button on the same form.
You have a good idea post here already.
You could also say place a function in the before insert event. This event ONLY fires when you start typing into a NEW reocrd, and it becomes dirty.
So, maybe this:
Private Sub Form_BeforeInsert(Cancel As Integer)
Dim rstPrevious As DAO.Recordset
Dim strSQL As String
strSQL = "SELECT TOP 1 * FROM tblPeople ORDER BY ID DESC"
Set rstPrevious = CurrentDb.OpenRecordset(strSQL)
' auto file out some previous values
If rstPrevious.RecordCount > 0 Then
Me.Firstname = rstPrevious!Firstname
Me.LastName = rstPrevious!LastName
End If
End Sub
And some good ideas in say having a "list" or "array" of controls/fields to setup, so you don't have to write a lot of code. (as suggested in the other post/answer here)

How to transfer data from one table to another in Access VBA?

I thought this would be easy....that will be my famous last words!
I have an access database that is used to keep track of company vehicles. I would like the form used by associates to retain more information that they do not have to see the information populated. I have created a form (fTripInformation) that will allow them to select the company vehicle used for a work trip from a combo box (CarID). Vehicles are numbered for convenience (1-10). What I would like to happen is the associate selects the vehicle number from the combo box drop down menu. When that happens the corresponding information associated with the vehicle (Make,Model, Year, Color, etc...) is added to the temp table (tTemp) for this form from the vehicles table (tVehicles). This way the associate only see the vehicle #, yet all the other information will be recorded when the temp table information is transferred to the actual record table.
Any suggestions on how to do this?
This what I did:First, I redefined the Row Source Query the Combobox uses to include all the related information and updated the total Column Count in the Properties Sheet. Then, added the following code:
Private Sub CarID_Change()
Dim Rs As Recordset
Set Rs = CurrentDb.OpenRecordset("tTemp")
Rs.Edit
Rs!CarID = Me.CarID.Column(0)
Rs!Make = Me.CarID.Column(1)
Rs!Model = Me.CarID.Column(2)
Rs!Year = Me.CarID.Column(3)
Rs!Color = Me.CarID.Column(4)
Rs.Update
End Sub
Private Sub CarID_DblClick()
Dim Rs As Recordset
Set Rs = CurrentDb.OpenRecordset("tTemp")
Rs.Edit
Rs!CarID = ""
Rs!Make = ""
Rs!Model = ""
Rs!Year = ""
Rs!Color = ""
Rs.Update
End Sub
This is, in fact, pretty easy! Download the demo file from the link below and you will have the concept that you described above. Just modify the code to achieve your specific goals.
http://www.mediafire.com/file/x9cuorenr7r9mi5/AccessAndSQL4_2000.mdb/file

Editing a record immediately after creating it

For my paste function, in certain cases, I need to be able to create a record and immediately edit it afterwards. This is because I need to know the value that gets generated for the AutoNumber field.
The error I get is "Update or CancelUpdate without AddNew or Edit." I marked where this error pops up in my code sample.
I pasted my entire paste function in case that it might help. Though the code that I am unsure of how to properly execute is in the bottom 4th (everything below the ***).
If you want to know exactly what I am trying to do feel free to read the rest of the post though it should be sufficient without.
Essentially what I am trying to do is for each record in my clipboard, I want to make a duplicate of it - copying values of all the fields. There are exceptions however. The ones of interest are the AutoNumber field, "ESDNodeID", and "ParentID" which is the ESDNodeID of the record that the record inherits from.
The clipboard (which contains the ESDNodeID and the ParentID) of the nodes that already exist which are being copied are sorted so that if a child record has a parent record, its parent is the next one in the list. So my idea is that I can use the AutoNumber value that gets generated for the record's id to find out what it will be for its parent id (which should just be its id + 1 since it is next in the for loop).
Public Function Paste(nodeID As Long)
Dim currScenarioID As Long
Dim i As Long
Dim saveParentIDs As Collection
Set saveParentIDs = New Collection
currScenarioID = Forms("Main")!Scenarios!ScenarioID
Dim rstSource As DAO.Recordset
Dim rstInsert As DAO.Recordset
Dim fld As DAO.Field
'We want to insert records into the ESDNodes table
Set rstInsert = CurrentDb.OpenRecordset("ESDNodes")
'We want to insert a record for each element in the clipboard
For i = 0 To UBound(clipboard)
'rstSource represents the record that we want to copy. Should only be 1 as ESDNodeID is unique.
Set rstSource = CurrentDb.OpenRecordset("SELECT * FROM ESDNodes WHERE ESDNodeID = " & clipboard(i)(0))
rstSource.MoveFirst
With rstInsert
'create a new record
.AddNew
'Want to copy all the fields
For Each fld In rstSource.Fields
With fld
If .Name = "ESDNodeID" Then
'Skip Autonumber field
'If the field is the ParentID
ElseIf .Name = "ParentID" Then
'If the clipboard has a NULL value that means the node selected is the Parent
If IsNull(clipboard(i)(1)) Then
rstInsert.Fields(.Name).value = nodeID
'If the parent ID has already been created for another node, we want to grab that ID
ElseIf Contains(saveParentIDs, CStr(clipboard(i)(1))) Then
rstInsert.Fields(.Name).value = saveParentIDs(CStr(clipboard(i)(1)))
'If neither of these conditions pass, the parentID is handled after the for loop
End If
'We want the active scenario id
ElseIf .Name = "ScenarioID" Then
rstInsert.Fields(.Name).value = currScenarioID
'Copy all other fields direcly from record
Else
rstInsert.Fields(.Name).value = .value
End If
End With
Next
'If the parent ID was not set above, that means we have not yet created the record corresponding to its parentID
'But because of how our clipboard is sorted, it will be the next one in the loop. Meaning that we can create this new record
'with an empty parentID, and then predict the id of its parent by simply adding 1 to its id
'*****************
.Update
.MoveLast
If Not IsNull(clipboard(i)(1)) Then
If Not Contains(saveParentIDs, CStr(clipboard(i)(1))) Then
!parentID = !ESDNodeID + 1 'ERROR HERE
saveParentIDs.Add !parentID, CStr(clipboard(i)(1))
.Update
End If
End If
.Close
End With
Next i
loadESDTreeView
End Function
You should use method .Edit before changing existing recordset fields.
Also don't close rstInsert inside For, it will fail on next row.
Few more things.
May be I didn't catch the whole idea, but if you moving to last row before editing it in order to just read ESDNodeID, generated after .AddNew, you don't need to use .Update with .MoveLast, you can read the new Id right after .AddNew, it is available.
Predicting of autonumber field value is bad idea, especially in multiuser environment.
Database integrity is very important, so ParentID should have foreign key constrain on ESDNodeID, in this case database won't allow you to insert not existing yet ESDNodeID. Try to review the logic of this procedure.

How to add check boxes to a form based on columns in table in Access?

I have a form in Access that allows a user to select a table from a combo box that lists all of the tables using VBA. Based on what table the user selects, I want a set of check boxes to be added to the form. Each check box would represent a column in the chosen table. I have a VBA function that will work with the columns that are chosen through the form after a button is pressed.
How would I make the check boxes appear after the user has chosen a table? I want this form to be applicable to tables that would be added in the future as well so I won't know the number of columns in advance. I can get check boxes to appear dynamically in a Userform but I've heard that they aren't commonly used in Access. I have also heard that there is a limit for the amount of controls that can be added to a form in its lifetime. Is there anyway of renaming controls so that I don't hit this limit?
This is the code that I use to populate the combo box:
Private Sub Form_Load()
Dim tbl As DAO.TableDef
Dim db As Database
Set db = CurrentDb
With Me![TableName]
For Each tbl In db.TableDefs
If Not (tbl.name Like "MSys*" Or tbl.name Like "~*") Then
.AddItem tbl.name
End If
Next
End With
End Sub
This is example code that can generate the check boxes in a Userform:
Private Sub RetrieveColumns_Click()
Dim TableName As String
Dim MethodName As String
Dim tdf As DAO.TableDef
Dim rs As Recordset
Dim fld As Field
Dim chkBox As MSForms.CheckBox
TableName = Me![TableName]
Set tdf = dbs.CreateTableDef(TableName)
Set rs = dbs.OpenRecordset(TableName)
With rs
For i = 0 To .Fields.Count - 1
Set chkBox = Me.Controls("Forms.checkbox.1", "CheckBox_" & i)
chkBox.Caption = .Fields(i).name
chkBox.Left = 5
chkBox.Top = 5 + ((i - 1) * 20)
Next i
End With
End Sub
The best solution would be a continuous form / datasheet that shows all table column names with an adjacent checkbox.
Create a table with ColumnName (Text) and Checked (Yes/No, Default No).
Then when a table is selected:
delete * from this table,
In your .Fields loop, insert all column names into this table,
and show the table as subform (or have the table selection combobox in the form header).
With this, you don't need any dynamic controls.
Alternatively, for a truly simple solution, use a multi-select listbox.
A listbox has the slightly obscure RowSourceType Field List.
The data is a list of field names from a table, query, or SQL statement specified by the RowSource setting.
So all you'd need is to set the RowSource property to the selected table name.

Trying to fill in data if Null to take the previous that has data

Newbie here and probably to this process. I'm trying to fill in data that is Null on the ticker field to take the previous known data. See my example below. I don't know if I should write in a query or something else on MS Access 2007.
So for example under 0897250D CH Equity is null or blank. I want to take the data before it and fill it in.
Sorry. Got my answer. I pretty much used the following....
Sub nullparts()
Dim i As String
Set myDb = CurrentDb()
Set MyRs = myDb.OpenRecordset("BB - HistoricalDividends")
MyRs.MoveFirst
Do While Not MyRs.EOF
MyRs.Edit
For Each myfld In MyRs.Fields
If myfld.Name = "Field1" Then
If MyRs(myfld.Name).Value <> "" Then
i = MyRs(myfld.Name).Value
Else
MyRs(myfld.Name).Value = i
MyRs.Update
End If
End If
Next myfld
MyRs.MoveNext
Loop
End Sub
Now I'm trying to accomplish 2 more things.
1) Create an Import From Excel button on the database
2) Once imported, remove all lines that have no data on F2 to be deleted.