Replicated Databases and Random Autonumbers
If your database was at one time replicated, then the tables will have picked up a number of special replication fields such as s_Generation, s_Lineage, s_GUID, or s_ColLineage. It is a good idea to remove these now unwanted fields prior to upsizing, and possibly convert any ReplicationID fields, if used as a primary key, to standard autonumbers.
A general feature of Access that is associated with replication is the option to have a random Autonumber sometimes used as a primary key. If, for example, in an Access database you have a table called Customer2 with a field NewId that was a random Autonumber, then if you look in CurrentDB.TableDefs("Customers2").Fields("NewId").DefaultValue, you can spot a function called GenUniqueID(); this is the internal function that Access uses to generate the random Autonumbers. One nice feature of the Upsizing Wizard is that it will automatically write the following trigger code (usp_GenUniqueID.sql) to generate the random Autonumber in SQL Server, and sets a default of 0 for the field NewId, which is the primary key:
CREATE TRIGGER [dbo].[T_Customers2_ITrig] ON [dbo].[Customers2] FOR INSERT AS SET NOCOUNT ON DECLARE @randc int, @newc int /* FOR AUTONUMBER-EMULATION CODE */ /* * RANDOM AUTONUMBER EMULATION CODE FOR FIELD 'NewId' */ SELECT @randc = (SELECT convert(int, rand() * power(2, 30))) SELECT @newc = (SELECT NewId FROM inserted) UPDATE Customers2 SET NewId = @randc WHERE NewId = @newc GO
The preceding approach, however, has a logical flaw: if an insert is from data in another table with multiple rows or using multiple values, then, as demonstrated in the code that follows, it generates duplicate primary key values of 0 and fails (remember the trigger fires after the insert). The good news is that this is very easily fixed, and the trigger code gives you all the information to write a SQL Server function to resolve this. This code is interesting because you want to create a generic function that uses the RAND function, but it turns out that you are not allowed to use the RAND system function inside a user-defined function. So, you must employ a cunning trick and wrap the RAND function inside a View, and then you are allowed to do this.
After allowing the Upsizing Wizard to convert the sample table Customer2, you can cause the previously described trigger to fail by using the following code:
INSERT INTO Customers2(CompanyName) VALUES('Test'), ('Test2') GO
Next, to resolve this problem, you create a View and then a function to use the View. In the following, notice that the first definition of the function will not work; however, the code starting from creating the View will work:
-- This will not work CREATE FUNCTION usp_GenUniqueID() RETURNS int AS BEGIN DECLARE @randc INT SELECT @randc = (SELECT convert(int, rand() * power(2, 30))) RETURN (@randc) END GO -- Create the view CREATE VIEW vw_Rand AS SELECT convert(int, rand() * power(2, 30)) As RandomSeed GO -- This will work CREATE FUNCTION usp_GenUniqueID() RETURNS int AS BEGIN RETURN (SELECT * FROM vw_Rand) END GO
To complete this, you need to drop the existing trigger (by using DROP), drop the existing default, and then add your new default:
-- Drop the trigger DROP Trigger T_Customers2_ITrig GO -- drop the default, your default name would be different to this example ALTER TABLE [dbo].[Customers2] DROP CONSTRAINT [DF__Customers__NewId__2C3393D0] GO ALTER TABLE [dbo].[Customers2] ADD
Allowing for changes to multiple rows in trigger code
The previous example demonstrates how trigger code should always be written to allow for multiple rows to be inserted, updated, or deleted; otherwise, you run the risk that at some point you perform an operation involving multiple rows and the application then generates errors. In some situations (although not in this case), if you cannot write the trigger for multiple rows, then you can consider using the following T-SQL to temporarily disable and then enable a trigger after completing the operation; this would only be recommended for use in maintenance and not while users are interacting with the system:DISABLE TRIGGER T_Customers2_ITrig ON Customers2 GO ENABLE Trigger T_Customers2_ITrig ON Customers2 GOThis example also shows the benefit in having constraints with easily managed names for triggers and other objects in the database.
In this tutorial:
- Upsizing Access to SQL Server
- Planning for Upsizing
- Boolean Data
- IMAGE, VARBINARY(Max), and OLE Data
- Cycles and Multiple Cascade Paths
- Replicated Databases and Random Autonumbers
- Unique Index and Ignore Nulls
- Timestamps and Row Versioning
- Schemas and Synonyms
- Upsizing Wizard and the SQL Server Migration Assistant
- Upsizing to Use an Access Data Project
- SSMA
- Comparing Table Conversion in the Upsizing Wizard and SSMA
- Comparing Query Conversion in the Upsizing Wizard and SSMA
- Developing with Access and SQL Server
- Stored Procedures and Temporary Tables
- Handling Complex Queries
- Performance and Execution Plans
- SQL Server Profiler