I have two tables named [Insert_Record] and [Delete_Record] in MS Access. Both tables have the same fields but one table has records whereas another table has no record.
Question: I want, whenever I delete any record from the table [Insert_Record] that entire record should automatically insert into another table, i.e: [Delete Record].
How can I accomplish this?
Access 2010 introduced event-driven Data Macros that are similar to triggers. The process described in the question can easily be done with an After Delete data macro on the [Insert_Record] table:
As I understand it, Access doesn't have triggers.
The best you can probably do is put this sort of logic into the forms that edit the table. In other words, handle the deleted event at the form level and put your insert logic there.
If you want triggers, you'll want to use a proper RDMS that supports them (MySQL, MS SQL, Oracle, many others).
EDIT: Another way to do this (which may or may not work for you) would be to add a boolean column 'IsDeleted'. That way, you can just logically delete a record instead of moving it to another table. The downside of this approach is the deleted records stay in the main table which could cause performance woes if there are lots of deletes.
Create an append query, to add records into the second table, that is executed in the On Delete Confirm event of the form you are using to delete the record from the first table.
I imagine this 'Delete' is button driven...
So program the button to first Insert the record into one table before deleting it.
Just add the VBA for the insert above the delete.
Another method which nullifies your need for another table entirely is just one column that is a boolean... Active Yes/No (Default to yes.)
If Active then it is not actually deleted when they hit delete, just set the flag to False then nothing is actually deleted, no SQL code is required and there is no risk, you can even have a column with the user who updated it print in another column
Me.Active = False
Me.UserName = 'CurrentUser Location here
Me.RecordSet.Requery
Then there is no risk of actually losing anything is there and you have a record of who flagged it last.
In the continuous form just add the Where flag Active = True then no false flags will show and it looks to the user base as if it was deleted.
Related
Access 2013
I have a simple lookup table that twice in the last month a record (different record on each occasion) has disappeared.
Since the auto id of the table is used as the foreign key I am forced to drop the table and recreate it.
I don't provide any delete functionality at all to users in the Access DB and especially not to the lookup table.
I have done a global search for all references to the table in my VBA code and found it was used several times but only in select statements.
At the moment I suspect a user is fiddling with something they don't fully understand and they are inadvertently deleting the record.
What else can I do determine the cause of this problem?
Can I make the lookup table read only?
Look at the relationships window. Look at the linked tables in question and see if there is a 'Cascade Deletes' checked. If so, deleting one record will delete related records.
It turned out to be a second lookup table that had a temporary relationship defined in the query that was not required. i.e: no columns were based upon this unnecessary table. This unnecessary lookup table was simply being used as a row source for a combobox on the form.
I discovered this after I noticed the form data type was set to Dynaset (Inconsistent Updates). Why? When I tested the form with normal Dynaset the form refused to perform any updates!
When I removed the unnecessary lookup table from the query I was able to set the data type back to Dynaset with updates continuing to work and deletions no longer cascading through to my first and required lookup table.
In addition the unnecessary lookup table was actually a query that was referencing a table in another database.
I want to be able to track any changes made to any of the fields in an existing table. The scenario is as follows:
the user opens the datasheet view for an existing table in MS Access 2007/2010
updates values in 2 fields and 5 rows (10 cells)
saves the table (overwrites it)
I want to be able to push the changes (10 changes/rows) to a new table and then be able to open it in datasheet view to refer to it. Is there a way I can do this in access without using form?
In 2010 and later you can use data macros, or what often other systems called table triggers.
You cannot in 2007.
Keep in mind that your use of the term “save table” is VERY wrong in the context here. In fact VERY wrong in the context of most databases. The user does not “save” the table.
You can ONLY edit ONE ROW at a time in a datasheet. When you move off that record then the ROW is saved (not the table). , and if you move off, then the record is saved.
In 2010 and later, you have use of table procedure code. This thus will allow one to use a table trigger.
So 2010 and later does support table triggers and store procedure code. But since your question in includes Access 2007, then my suggestion to use table triggers (called data macros) may not work unless you can restrict users to 2010 and beyond.
In the follow example, when a user updates a row, then a “audit” table is updated with the user information. The function fosusername() is in fact a VBA function. This code is called from the before change update event for the table.
Of course the problem here is the VERY basis of your question assumes that users save a table – they don’t, they edit + save “single” records, or a single row at a time. So figuring “out” how a user is done would certainly be a challenge. So while code can be run when the users edit data, having table code run “when” the user closes the table is not possible.
As the end of the day, it likely best you create a datasheet that looks just like the table, and then change a few settings to “lock down” and prevent the user from using tables directly.
As far as I know there is no solution for this without using a form. I do not know of events (like a change or dirty event) for the actual data tables.
In a form you could use the dirty property to make sure you fetch the data. You can make the form look like a datahseet with DoCmd.RunCommand acCmdDatasheetView or set the form in datasheet view on default.
In a Related Tables, you need to enter a FK to link to the parent table.
You either key in the FG from memory !!!!!! or use help.
The standard procedure is to design the field as combo box AND use SQL to select the field that will help to input the ID of the parent table. So, if you want to input foreign key 3 , the combo box will display what 3 is then when you selected Access will insert 3 for you.
My question is since I can later on, edit the table and change the the value to another value, and mess the whole thing up!!! HOW can I lock my first choice so it will stay unedited ?
My second question is: Is this the only way to input the FK if you do not remember the exact ID number or there are thousands of records in the parent table.?
Re. #1
In Access 2010 you could use a trigger to check whether the value has been used in another table as a FK and not allow the change. In previous versions all you could do, would be to hide the navigation pane or make the table hidden.
Re. #2
You could use a listbox (basically the same idea as a combobox).
You could include the lookup table in the underlying query, but then there is more danger of the values getting changed, which is exactly what you were worried about in the first question.
Stick with the combobox.
You can create different versions of a form. One might have Allow Edits, No, another might be Data Entry, Yes, meaning that it can only be used to create new records.
However, suppose someone is creating a new record and they select the wrong FK by mistake. How can they correct this? You could use VBA to produce a confirmation dialog in the first instance, and perhaps an Undo button where VBA would deliberately perform the Undo. Access 2010 also has Data Macros (equivalent to triggers) that you might use to store the old and new values, and other information, when a user changes a FK value.
Access does not implement user-level security, so the user can still open and change data in the raw-data tables. You should, however, have enforced referential integrity on the table-join, so that a user cannot enter a FK that doesn't already exist as a PK in the related table.
In summary, you could add additional columns to the combobox to help the user select the correct item, and you might consider an additional confirmation dialog (a MsgBox) before a record is saved into the data-table. But you cannot prevent people from making mistakes. (Enforcing Referential Integrity will prevent nonsense data from being entered.)
Second question:
A combobox (or possibly a listbox) is the easiest way for users to enter a FK value, without relying on memory. An alternative would be to use a button (or other control and event) that opens a secondary form. This form might have some filtering features to help the user find the correct FK value. When this form is closed you would then need to write some code to update the relevant control on the main form.
Whenever a record is deleted or updated on a form, I want to save its old values in a history table (let's call it Revised). I guess I have to do the following:
For record changes:
use the BeforeUpdate event to save the data somewhere (collection ? array ? or to a recordset -the Revised table- without saving-yet ?)
use the AfterUpdate event to add/save that data to the Revised table
For Deletions:
use the OnDelete event to save the data - but again how ? several records could be deleted at once since the form (a subform in fact) is in datasheet view
use the AfterDelConfirm to add that data to the Revised table.
Do you have any clues, comments or links for this ?
This is all in a "pure Access" (no SQL Server) at the moment.
Many thanks !
Edit: as usual, properly asking the question gaves me ideas:
option 1
use the BeforeUpdate or the OnDelete to build the SQL statement, and use the AfterUpdate or the AfterDelConfirm to Execute the SQL statement. But that won't work for multiple deletions ?
option 2
have the Revised recordset defined at form level, insert the record "Before" but only Update "After". Again, problem with multiple deletes.
I've successfully used a variation of Allen Browne's approach in a couple of different projects. Check out his website for more details:
Creating an Audit Log
His solution uses temp tables and four generic function calls to handle the issue with multiple deletes.
Another approach I have considered more recently, but have not had an opportunity to actually implement, would be to use transactions to perform the change tracking. The basic algorithm would be:
use BeginTrans on the workspace prior to making any changes
in the OnDelete event
perform the deletions in code executing Delete queries against the workspace from step 1
add a record to your change auditing table
in the BeforeDelConfirm event
set Cancel = True
display your own Confirmation dialog
if user confirms then CommitTrans on workspace
otherwise Rollback the transaction on the workspace
Similar approach for Updates/Inserts. This would avoid the need for temporary tables/arrays/collections, etc. but I haven't fully thought through everything. The devil may be in the details.
An "easy" and generic solution, which could be implemented for multiple tables, would be to have a tracking table, made of the following:
Track_Table
==================================================
id_track as primary key
id_table as name of the table which has been updated
id_primaryKey as the record identifier (the PK of the updated record)
changeType, being either DEL or UPDATE
changeDate, as dateTime value
fieldName, as text
oldValue, as text or memo
newValue, as text or memo
if you have to identify the user who did the update, just add
userId
in your table ...
You could then create some generic "before update" and "after update functions" to be called on selected form's beforeUpdate and afterUpdate events. The beforeUpdate fonction will store the old value in a variable, while the afterUpdate function will populate the missing data and insert a new record in the track table.
You will have to find a way to find out the right\corresponding table name and field name. This could be difficult if you are using views or field aliases to display your data in forms.
Of course, all tables to be followed must have a primary key so you can follow changes at the record level. PKs set on multiple fields will surely be problematic ....
oldValues and newValues will have to be converted as text so you can store them in a text or memo field
I have table in MS Access that has an AutoNumber type in field ID
After inserting some rows, the ID has become 200
Then, I have deleted the records in the table. However, when I tried to insert a new row, I see that the ID starts with 201
How can I force the ID to restart with 1, without having to drop the table and make new a new one?
In Access 2010 or newer, go to Database Tools and click Compact and Repair Database, and it will automatically reset the ID.
You can use:
CurrentDb.Execute "ALTER TABLE yourTable ALTER COLUMN myID COUNTER(1,1)"
I hope you have no relationships that use this table, I hope it is empty, and I hope you understand that all you can (mostly) rely on an autonumber to be is unique. You can get gaps, jumps, very large or even negative numbers, depending on the circumstances. If your autonumber means something, you have a major problem waiting to happen.
In addition to all the concerns expressed about why you give a rat's ass what the ID value is (all are correct that you shouldn't), let me add this to the mix:
If you've deleted all the records from the table, compacting the database will reset the seed value back to its original value.
For a table where there are still records, and you've inserted a value into the Autonumber field that is lower than the highest value, you have to use #Remou's method to reset the seed value. This also applies if you want to reset to the Max+1 in a table where records have been deleted, e.g., 300 records, last ID of 300, delete 201-300, compact won't reset the counter (you have to use #Remou's method -- this was not the case in earlier versions of Jet, and, indeed, in early versions of Jet 4, the first Jet version that allowed manipulating the seed value programatically).
I am going to Necro this topic.
Starting around ms-access-2016, you can execute Data Definition Queries (DDQ) through Macro's
Data Definition Query
ALTER TABLE <Table> ALTER COLUMN <ID_Field> COUNTER(1,1);
Save the DDQ, with your values
Create a Macro with the appropriate logic either before this or after.
To execute this DDQ:
Add an Open Query action
Define the name of the DDQ in the Query Name field; View and Data Mode settings are not relevant and can leave the default values
WARNINGS!!!!
This will reset the AutoNumber Counter to 1
Any Referential Integrity will be summarily destroyed
Advice
Use this for Staging tables
these are tables that are never intended to persist the data they temporarily contain.
The data contained is only there until additional cleaning actions have been performed and stored in the appropriate table(s).
Once cleaning operations have been performed and the data is no longer needed, these tables are summarily purged of any data contained.
Import Tables
These are very similar to Staging Tables but tend to only have two columns: ID and RowValue
Since these are typically used to import RAW data from a general file format (TXT, RTF, CSV, XML, etc.), the data contained does not persist past the processing lifecycle
I think the only ways to do this is outlined in this article.
The article explains several methods. Here is one example:
To do this in Microsoft Office Access 2007, follow these steps:
Delete the AutoNumber field from the main table.
Make note of the AutoNumber field name.
Click the Create tab, and then click Query Design in the Other group.
In the Show Table dialog box, select the main table. Click Add, and then click Close.
Double-click the required fields in the table view of the main table to select the fields.
Select the required Sort order.
On the Design tab, click Make Table in the Query Type group. Type the new table name in the Table Name box, and then click OK.
On the Design tab, click Run in the Results group.
The following message appears:
You are about to paste # row(s) into a new table.
Click Yes to insert the rows.
Close the query.
Right-click the new table, and then click Design View.
In the Design view for the table, add an AutoNumber field that has the same field name that you deleted in step 1. Add this AutoNumber
field to the new table, and then save the table.
Close the Design view window.
Rename the main table name. Rename the new table name to the main table name.
I always use below approach. I've created one table in database as Table1 with only one column i.e. Row_Id Number (Long Integer) and its value is 0
INSERT INTO <TABLE_NAME_TO_RESET>
SELECT Row_Id AS <COLUMN_NAME_TO_RESET>
FROM Table1;
This will insert one row with 0 value in AutoNumber column, later delete that row.