I need to change the primary key of a table to an identity column, and there's already a number of rows in table.
I've got a script to clean up the IDs to ensure they're sequential starting at 1, works fine on my test database.
What's the SQL command to alter the column to have an identity property?
One option to add an identity to the table is to add a new column to the table and set it as an identity. This is possible through the Alter statement. However, if we want to set the identity to an already existing column in the table, we cannot use this DDL command.
To add an IDENTITY column to a table, the table must be at a top level. You cannot add an IDENTITY column as the column of a deeply embedded structured datatype. Adding a column does not affect the existing rows in the table, which get populated with the new column's default value (or NULL).
You can't alter the existing columns for identity.
You have 2 options,
Create a new table with identity & drop the existing table
Create a new column with identity & drop the existing column
Approach 1. (New table) Here you can retain the existing data values on the newly created identity column. Note that you will lose all data if 'if not exists' is not satisfied, so make sure you put the condition on the drop as well!
CREATE TABLE dbo.Tmp_Names ( Id int NOT NULL IDENTITY(1, 1), Name varchar(50) NULL ) ON [PRIMARY] go SET IDENTITY_INSERT dbo.Tmp_Names ON go IF EXISTS ( SELECT * FROM dbo.Names ) INSERT INTO dbo.Tmp_Names ( Id, Name ) SELECT Id, Name FROM dbo.Names TABLOCKX go SET IDENTITY_INSERT dbo.Tmp_Names OFF go DROP TABLE dbo.Names go Exec sp_rename 'Tmp_Names', 'Names'
Approach 2 (New column) You can’t retain the existing data values on the newly created identity column, The identity column will hold the sequence of number.
Alter Table Names Add Id_new Int Identity(1, 1) Go Alter Table Names Drop Column ID Go Exec sp_rename 'Names.Id_new', 'ID', 'Column'
See the following Microsoft SQL Server Forum post for more details:
How to alter column to identity(1,1)
In SQL 2005 and above, there's a trick to solve this problem without changing the table's data pages. This is important for large tables where touching every data page can take minutes or hours. The trick also works even if the identity column is a primary key, is part of a clustered or non-clustered index, or other gotchas which can trip up the the simpler "add/remove/rename column" solution.
Here's the trick: you can use SQL Server's ALTER TABLE...SWITCH statement to change the schema of a table without changing the data, meaning you can replace a table with an IDENTITY with an identical table schema, but without an IDENTITY column. The same trick works to add IDENTITY to an existing column.
Normally, ALTER TABLE...SWITCH is used to efficiently replace a full partition in a partitioned table with a new, empty partition. But it can also be used in non-partitioned tables too.
I've used this trick to convert, in under 5 seconds, a column of a of a 2.5 billion row table from IDENTITY to a non-IDENTITY (in order to run a multi-hour query whose query plan worked better for non-IDENTITY columns), and then restored the IDENTITY setting, again in less than 5 seconds.
Here's a code sample of how it works.
CREATE TABLE Test ( id int identity(1,1), somecolumn varchar(10) ); INSERT INTO Test VALUES ('Hello'); INSERT INTO Test VALUES ('World'); -- copy the table. use same schema, but no identity CREATE TABLE Test2 ( id int NOT NULL, somecolumn varchar(10) ); ALTER TABLE Test SWITCH TO Test2; -- drop the original (now empty) table DROP TABLE Test; -- rename new table to old table's name EXEC sp_rename 'Test2','Test'; -- update the identity seed DBCC CHECKIDENT('Test'); -- see same records SELECT * FROM Test;
This is obviously more involved than the solutions in other answers, but if your table is large this can be a real life-saver. There are some caveats:
There's a good article on TechNet detailing the requirements above.
UPDATE - Eric Wu had a comment below that adds important info about this solution. Copying it here to make sure it gets more attention:
There's another caveat here that is worth mentioning. Although the new table will happily receive data from the old table, and all the new rows will be inserted following a identity pattern, they will start at 1 and potentially break if the said column is a primary key. Consider running
DBCC CHECKIDENT('<newTableName>')
immediately after switching. See msdn.microsoft.com/en-us/library/ms176057.aspx for more info.
If the table is actively being extended with new rows (meaning you don't have much if any downtime between adding IDENTITY and adding new rows, then instead of DBCC CHECKIDENT
you'll want to manually set the identity seed value in the new table schema to be larger than the largest existing ID in the table, e.g. IDENTITY (2435457, 1)
. You might be able to include both the ALTER TABLE...SWITCH
and the DBCC CHECKIDENT
in a transaction (or not-- haven't tested this) but seems like setting the seed value manually will be easier and safer.
Obviously, if no new rows are being added to the table (or they're only added occasionally, like a daily ETL process) then this race condition won't happen so DBCC CHECKIDENT
is fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With