Maintaining AutoNumber functionality in a form while using SQL backend - ms-access

Currently, I am trying to move from an Access backend to a SQL backend for my database while still keeping the Access form as the frontend. This is done via linked tables with ODBC connections to my backend. The form is designed to add new records to the table. The problem is with AutoNumber. The ID was set as an AutoNumber and the form would simply display (New) while waiting for the user to add the remaining columns. I have set up my SQL data with the IDENTITY property, so it will increment once a new record is created. However, I cannot get the form to behave the same way as it did because, even though the backend will automatically add the next sequential ID, I cannot automatically fill that data into the form prior to a user actually saving the form data. Is there a way maintain the form functionality that AutoNumber provides?
TL;DR: Form is not working correctly after AutoNumber is changed to Number.

Well, there are "rare" cases in which you actually need the autonumberr before you save the record. For example, if you have a sub-form, then Access ALWAYS does a automatic save of the main record, and thus the autonumber is and will have been created .So a main form, and sub form (child table) will work fine, and do so without code.
Now, there are some cases in which you need the autonumber. Say you have some "code" that needs to run and spit out some child records.
The general approach is to simply execute a record save at that point in time.
So, say there is a button, or some code you need to run in the form, and you NEED the PK autonumber?
You can use this code:
If isnull(me!ID) = true then
me.dirty = false ' force record save - autonumber now created
end if
the record for above to work will have to be "dirty", but in near all cases, this tends to be the case. The "rare" exceptions would suggest that you could check me.IsNewRecord, but in most cases the above bit of code will suffice.
I can't really imagine that the "display" of some autonumber is oh so important WHEN the user is starting to enter data.
However, if you want the autonumber to appear after ANY keypress (data entry on the form)?
Simply put this line of code in the after insert event:
me.dirty = false
So, now when looking at a form, the FIRST key press by the user in any text box will force the autonumber to be generated and appear. However, it is a VERY bad practice to assign any meaning of the autonumber ID to the end users. In fact that ID should in most cases be hidden.
the only issue or downside of above is of course that if you have any required columns, then the above may error out or case an issue.

Solution
For this problem, I needed to set my identity_insert to ON in the SQL backend. Here is the code to do so:
SET IDENTITY_INSERT tableName ON;
Also, if you get the error: Table <Table Name> does not have the identity property. recreate the table with the autoincrementing column having an identity.
Example:
CREATE TABLE new_employees (id_num int IDENTITY(1,1), fname varchar (20), minit char(1), lname varchar(30));
More on IDENTITY in Microsoft offical documentation: here

Related

Access Forms - Catch 22 trying to insert data to multiple related tables

I'm new to Access but have many years experience using "enterprise" databases. I'm having trouble implementing a simple task and suspect that my preconceptions are causing me to miss the point so I'm asking for some help.
The simple task is using Access forms for Data Entry that populates two tables: Customer and CustomerAddress (can be multiple addresses per customer).
The Customer table has primary key CustomerID. The CustomerAddress table has primary key CustomerAddressID and CustomerID as a foreign key with RI on that relationship.
Form1 is bound to the Customer table. After entering info for various fields, the user can click a button to display Form2 which is bound to CustomerAddress and enables data entry for multiple addresses.
Form1 passes the CustomerID (assigned by Access) to Form2. Form2 is not a subform due to size but it could be if that would solve the problem.
In a perfect world, I would want all the new data from both Form1 and Form2 to be committed together. Presumably I could do this using unbound forms and code the insert statements inside a single transaction.
Question 1: Is there a way to do this using bound forms?
If I use Form1 without ever clicking the "Address" button then a row is successfully added to the Customer table. The problem comes when trying to add CustomerAddress row(s) in Form2 before the Customer row has been added to the table.
In Form2, if the CustomerID is not used, then there is an insert error because a CustomerAddress row cannot be added without a CustomerID.
If the CustomerID is used, then there is an insert error because the CustomerID does not exist on the Customer table yet (although the ID appears to have been "reserved").
It's not practical to force the Customer row to be added before opening Form2 as some Customer required fields are not present at that point in the workflow.
Question 2: Is there a way round this? It seems like a common requirement.
I could work around this by removing the RI so that the CustomerAddress rows can be added first, but this seems poor DB design and I'd also need clean-up logic for the case where the Customer add is subsequently cancelled.
As mentioned, I'm probably missing the point and there's a better approach. Any help much appreciated.
I think you would have the same problem regardless of which RDBMS you used if you are enforcing referential integrity. The CustomerID needs to exist in the Customer table before a record can exist in the CustomerAddress table. I'm guessing that CustomerID is an autonumber. What is probably confusing is that Access does immediately 'reserve' an autonumber as soon as a new record is started. However, it does not exist in the Customer table. If that record is never saved, that autonumber value is lost and the next record would get the next number. Requiring an address to be completed before saving the Customer record, sounds like a design problem. It doesn't seem logical. Personally, I would re-think the design. That said, one solution would be to create a temp table for addresses and bind your CustomerAddress form to that. Then, when the customer record is saved, you would run an append query to add the new addresses to the CustomerAddress table. Remember, though, that if a user enters some addresses and the Customer record is never saved, all of that data entry is lost.
With regard to your statement:
It's not practical to force the Customer row to be added before opening Form2 as some Customer required fields are not present at that point in the workflow.
You are correct that you won't be able to insert the Customer row if the "required but not yet known" fields have their Required property set to Yes (i.e., NOT NULL). However, with Access 2010 and later you could use an event-driven data macro to make such fields "not required" on Insert but "required" on Update. In your case, you could
allow the Customer record to be inserted without the "eventually required" fields,
allow the addition of CustomerAddress records (with RI enabled), and then
re-open the Customer record for updating, with the data macro now enforcing the "required" status of the other fields.
The Before Change data macro might look something like this:
I think the answer lies within your Question 1. Yes there is a way to do this with bound forms, and would be the approach I would use.
Form1 is bound to the Customer table. Add Form 2 onto Form 1 as a subform. Select / highlight Form 2, goto the properties sheet. On the Data tab, set the Link Master Field to CustomerID and Link Child Fields to CustomerID.
Then after any action taken on Form 1, requery Form 2.
I think this will get you started and at least should give you an idea on how to proceed once completed.
I fail to see why both must be or one would even want to submit both customer and a record from the child table at the same time.
What about when you want to add an additional address, then that add address button and code now will have to check + test if the main customer already exists, and use separate logic to deal if the customer record exists, or does not. And what about if a user has entered the customer name and info, and does not yet have the address as the customer on the phone might have just moved and has to call back with the new address. I can think of another half dozen cases in which building a design in which both customer and customer address records MUST be added at the same time.
As others noted, you going to have this issue with any relational database, including Access. I should also point out that Access works EXACTLY the same here if you use Access as a front end to SQL server or Oracle.
So it makes little sense to build a different UI and coding process for adding the customer and the address, and then a DIFFERENT set of rules and UI for adding additional address over time. So build a form to search + find a customer, and add that customer and display that customer. Once that process is done, then you add the process (and UI part) to allow the user to view, or add address to that customer. I don’t think I ever seen a UI with separate forms to add/edit customer and a separate form to allow additional address to have some requirement that they are submitted together at the same time.
If the user bails out of the Address because they realize the address is not available, wrong etc., this should not suggest that the customer information entered so far be dumped and not added. If the user after bailing from adding the address (say close or cancel that form), then you return to the customer form. If the user decides at that point in time for some strange reason they do NOT want the customer in the system anymore, then provide a delete button.
If the business rules are that one MUST ALWAYS have at least one address in all cases, then upon closing the customer form you don’t allow the form to be closed and request that that user enter at least one address for this user. And perhaps they bumped or hit the wrong key to exit the address enter part – no need to penalize that user and force them to re-enter the customer info. So don’t let them exit the customer form until they enter an address or provide them (suggest to the user) they can delete/remove the customer fi they want to exit.
If you use a form + sub form setup, then zero code is required for the relational part. And a tab control gets you both forms can be near full screen size. (no need to launch a separate form).
Of course you could write a bunch of code and placed everything in a transaction, but such is a waste of coding time for no benefits and amounts to theft of company hours and time when not required.

MS Access "record changes" log

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

How to restart counting from 1 after erasing table in MS Access?

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.

A little Access VBA help? Validating against duplication of a non key field?

I am adding a part name to the database using a form, What code do I put behind the Add part button to validate against duplicate part names? (part number is the primary key) I think I need another recordset to search and compare the table but i'm a bit lost, any help would be great.
Private Sub btn_add_Click()
rs_parts.AddNew
With rs_parts
!partrno = lbl_partno.Caption
!Name = txt_name
rs_parts.update
end with
I've discussed my approach to this before, and given an example form for adding a new record.
I use an unbound form to collect the information needed to create the new record, and have it check for duplicates, and present a list of them to the user so the user can decide what to do.
In this case, it sounds like a unique index is in order, so you won't really need to worry about close matches. I would probably still use an unbound form to capture the new value and run the check before even attempting to add it. In that case, I'd just notify the user that it's a dupe.
Should you add a unique index to the part name field?
If you want, you can create a query on your part table with one column for the name and a parameter for the criteria they've entered in txt_name. Then pass the value they entered as a parameter to the query and see if you get any results.

Form Recordset in Access 2007 Returning no data

I'm building a form in access that has a several sub-forms in a tab control The sub-forms are working fine, but when I set the parent form's Record Source property and bind a few Text Boxes to the data, I find that a null recordset has been returned.
I've tried this with several different queries (each of which works in a separate query designer) to no avail. I've also tried binding the form to a Table directly and a null recordset is still returned.
I've checked the filters and they are turned off.
If the Text Boxes that are bound to data are Locked and Disabled then the query will return a null dataset by default.
The form's 'Data Entry' property also needs to be set to false. Setting it to true will make the form for data entry only. This prevents any data that might be returned by the background dataset from appearing when the form loads.
Have you tried executing a Requery on your parent form:
Me.RecordSource = "someQuery"
Me.Requery
?
Can you unlock and/or enable the fields before setting the data source?
Try checking the relationships in your tables especially if you used the auto "look-up" option to create a relationship.
Open your tables in design view and check if your foreign keys(FK) are indexed appropriately or not (under field properties).
If they are indexed, ensure you have the correct type of index for that particular field. Example; TABLE A with following columns PatientID(PK), PatientName and TABLE B with columns ID, PatientHeight, PatientWeight, PatientBP, PatientID(FK)
For such Table B, if you have the "PatientID(FK) indexed with "duplication OK", you will get RecordSet error when using those fields related to this table in the form especially if the form JOINS fields from multiple tables.
In summary, try checking the indexing of your fields especially if they correspond to their type of relationship