158

We have a 5GB table (nearly 500 million rows) and we want to remove the identity property on one of the column, but when we try to do this through SSMS - it times out.

Can this be done through T-SQL?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Conrad Jagger
  • 2,223
  • 4
  • 19
  • 15
  • 1
    Can you post the schema of the table here? – Al W Nov 22 '11 at 16:37
  • 1
    I'm sure there are excellent reasons that SQL Server doesn't support removing an identity property from a column via a simple ALTER TABLE ... statement, but it's nevertheless making me sad currently that that's the case. – Jon Schneider Jan 16 '17 at 23:26
  • Possible duplicate of [SQL Server how to drop identity from a column](https://stackoverflow.com/questions/702745/sql-server-how-to-drop-identity-from-a-column) (which is seems specific to [[tag:sql-server-2005]], but suggests the same approaches anyway) – Bergi Mar 25 '23 at 16:45

12 Answers12

176

You cannot remove an IDENTITY specification once set.

To remove the entire column:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

Information about ALTER TABLE here

If you need to keep the data, but remove the IDENTITY column, you will need to:

  • Create a new column
  • Transfer the data from the existing IDENTITY column to the new column
  • Drop the existing IDENTITY column.
  • Rename the new column to the original column name
yzorg
  • 4,224
  • 3
  • 39
  • 57
Adam Wenger
  • 17,100
  • 6
  • 52
  • 63
  • 4
    you can remove an Identity spec. In fact I had to do it yesterday using SSMS although not on 500 million rows. – Simon Nov 22 '11 at 16:46
  • 40
    @simon if you script out your changes, you'll see SSMS is actually creating a copy of the table w/o the identity property. – Code Magician Nov 22 '11 at 16:47
  • 4
    I just want to add to rename the new column to the original column's name. Also, if this `identity` column is used as part of a `foreign key` in another table, you're going to have to drop the constraints first, then take action as @AdamWenger mentioned about removing the identity `attribute/property` ..You can also look into this link for more details about removing the attribute only: http://blog.sqlauthority.com/2009/05/03/sql-server-add-or-remove-identity-property-on-column/ ..Good luck! – Nonym Nov 22 '11 at 17:01
  • 2
    To the person who down-voted - I would appreciate hearing what you did not like about my answer. – Adam Wenger Aug 11 '14 at 19:42
  • 2
    Take a look at Mark Sowul's answer below. Obviates the need to move data from column to column. Using Mark's answer you are only shuffling metadata. No big deal unless you are working on a table that has tens or hundreds of millions of rows. Plus Mark's answer prevents the relocation of the column in the table schema. I just tried it and it worked like a charm. Very clever. – Andrew Steitz May 31 '16 at 03:10
147

If you want to do this without adding and populating a new column, without reordering the columns, and with almost no downtime because no data is changing on the table, let's do some magic with partitioning functionality (but since no partitions are used you don't need Enterprise edition):

  1. Remove all foreign keys that point to this table
  2. Script the table to be created; rename everything e.g. 'MyTable2', 'MyIndex2', etc. Remove the IDENTITY specification.
  3. You should now have two "identical"-ish tables, one full, the other empty with no IDENTITY.
  4. Run ALTER TABLE [Original] SWITCH TO [Original2]
  5. Now your original table will be empty and the new one will have the data. You have switched the metadata for the two tables (instant).
  6. Drop the original (now-empty table), exec sys.sp_rename to rename the various schema objects back to the original names, and then you can recreate your foreign keys.

For example, given:

CREATE TABLE Original
(
  Id INT IDENTITY PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value ON Original (Value);

INSERT INTO Original
SELECT 'abcd'
UNION ALL 
SELECT 'defg';

You can do the following:

--create new table with no IDENTITY
CREATE TABLE Original2
(
  Id INT PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value2 ON Original2 (Value);

--data before switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

ALTER TABLE Original SWITCH TO Original2;

--data after switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

--clean up 
IF NOT EXISTS (SELECT * FROM Original) DROP TABLE Original;
EXEC sys.sp_rename 'Original2.IX_Original_Value2', 'IX_Original_Value', 'INDEX';
EXEC sys.sp_rename 'Original2', 'Original', 'OBJECT';


UPDATE Original
SET Id = Id + 1;

SELECT *
FROM Original;

For the record, as this has become increasingly popular, I have wanted to track down the original source (I didn't think of it myself), but of course I've long forgotten where I found the original idea. It may have been here; this was the only one I could find predating this answer (time flies, boys and girls): https://social.technet.microsoft.com/wiki/contents/articles/17738.sql-server-quick-way-to-remove-the-identity-property.aspx

Mark Sowul
  • 10,244
  • 1
  • 45
  • 51
  • 23
    This should be the accepted answer. It is the only real way to remove an identity column without massive data migration. – Vaccano Mar 07 '16 at 21:31
  • 6
    If using SQL Management Studio to script out the table, be sure to turn on Tools > Options > SQL Server Object Explorer > Scripting > Table and view options > Script indexes (False by default) – user423430 Nov 15 '16 at 23:58
  • 1
    This is great. Note that you do not need to use new names for the indices. As you can see in the code that renames an index, index names are scoped to the table, so you can have identical index names in two different tables. However, the same is not true for constraints and triggers. For those, you must use unique names, as shown here for indices. – P Daddy Nov 20 '18 at 17:24
  • 1
    Incidentally it's even easier if you just use a different schema for the second table -- then you don't need to rename anything (so you can pretty much copy and paste the 'generate table' script and just alter the schema name). Once done, you can use `ALTER SCHEMA [original schema] TRANSFER [new schema].[table]` – Mark Sowul Oct 31 '19 at 16:08
  • 1
    However, beware: `ALTER SCHEMA ... TRANSFER` does a lot more locking (https://www.brentozar.com/archive/2015/05/staging-data-locking-danger-with-alter-schema-transfer/) – Mark Sowul Oct 31 '19 at 16:29
  • @kuklei Steps 1 and 6 point out that you need to drop and recreate the foreign keys. If it takes less time to rebuild the entire table than it takes to recreate the foreign keys, then yes, the SSMS table designer is easier. But on large tables it takes a very long time to use the SSMS designer to rebuild the table (during which time the data is inaccessible), and sometimes it's impossible because it will fill up the transaction log, and/or because while the table is being rebuilt it takes up twice the space in the data file. – Mark Sowul Feb 14 '20 at 16:12
  • Sorry @MarkSowul. Missed the last few words in point 6 after the comma. Your solution is good for large tables and can be combined with the SSMS scripting capabilities to generate the drop and create of foreign key columns before and after the switch statement. Good one – kuklei Feb 17 '20 at 13:44
  • This is a brilliant solution, thanks (5 years later...). After step 4, once the original table is empty, I dropped the identity using SSMS's design option, and then ran ALTER TABLE [Original2] SWITCH TO [Original] to put all the data back in the original table. – Ethan1701 Nov 30 '20 at 12:44
  • 1
    Remeber to drop the Foreign Key constraints at the beginning and set again at the end of the process. Thanks! – Hagen Feb 18 '21 at 01:40
  • Don't Forget to script-out any Extended Attributes! And make sure to CHECK CHECK those FKs. And DEFAULT CONSTRAINT names (caught me on that one) must be unique per DB, so there's more sp_rename activity after Original-DROP. Saved me untold hours on a 500M-row, 70 FK central table. HUGE THANKS, @MarkSowul – SAinCA Nov 18 '22 at 22:52
65

This gets messy with foreign and primary key constraints, so here's some scripts to help you on your way:

First, create a duplicate column with a temporary name:

alter table yourTable add tempId int NOT NULL default -1;
update yourTable set tempId = id;

Next, get the name of your primary key constraint:

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'yourTable';

Now try drop the primary key constraint for your column:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

If you have foreign keys, it will fail, so if so drop the foreign key constraints. KEEP TRACK OF WHICH TABLES YOU RUN THIS FOR SO YOU CAN ADD THE CONSTRAINTS BACK IN LATER!!!

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'otherTable';
alter table otherTable drop constraint fk_otherTable_yourTable;
commit;
..

Once all of your foreign key constraints have been removed, you'll be able to remove the PK constraint, drop that column, rename your temp column, and add the PK constraint to that column:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;
alter table yourTable drop column id;
EXEC sp_rename 'yourTable.tempId', 'id', 'COLUMN';
ALTER TABLE yourTable ADD CONSTRAINT PK_yourTable_id PRIMARY KEY (id) 
commit;

Finally, add the FK constraints back in:

alter table otherTable add constraint fk_otherTable_yourTable foreign key (yourTable_id) references yourTable(id);
..

El Fin!

Briguy37
  • 8,342
  • 3
  • 33
  • 53
  • pay attention to your sql server version, I tried in azure sql server, not all operations here is supported in azure version sql. – dasons Mar 31 '15 at 14:35
  • Thanks for the answer! Helped me a lot! You just should add a verification for indexes that reference the column. Something like: `Select t.name 'Table', i.name 'Index', c.name 'Column', i.is_primary_key, i.is_unique From sys.tables t Inner Join sys.indexes i On i.object_id = t.object_id Inner Join sys.index_columns ic ON ic.object_id = i.object_id And i.index_id = ic.index_id Inner Join sys.columns c ON ic.object_id = c.object_id and ic.column_id = c.column_id Where t.name = 'tableName' Order By t.name, i.name, i.index_id, ic.index_column_id` – Alexandre Junges Oct 29 '15 at 11:52
  • good trick, helped me lot, But one point, I want to mention here is that, When we add a new duplicate column as mentioned in 1st step, it added at the end but generally we want that our primary key column i.e. Id to be the 1st column in that table. So is there any workaround to that? – Ashish Shukla Mar 15 '16 at 08:58
24

I just had this same problem. 4 statements in SSMS instead of using the GUI and it was very fast.

  • Make a new column

    alter table users add newusernum int;

  • Copy values over

    update users set newusernum=usernum;

  • Drop the old column

    alter table users drop column usernum;

  • Rename the new column to the old column name

    EXEC sp_RENAME 'users.newusernum' , 'usernum', 'COLUMN';

liberty_
  • 241
  • 2
  • 3
13

Following script removes Identity field for a column named 'Id'

Hope it helps.

BEGIN TRAN
BEGIN TRY
    EXEC sp_rename '[SomeTable].[Id]', 'OldId';

    ALTER TABLE [SomeTable] ADD Id int NULL

    EXEC ('UPDATE [SomeTable] SET Id = OldId')

    ALTER TABLE [SomeTable] NOCHECK CONSTRAINT ALL

    ALTER TABLE [SomeTable] DROP CONSTRAINT [PK_constraintName];
    ALTER TABLE [SomeTable] DROP COLUMN OldId
    ALTER TABLE [SomeTable] ALTER COLUMN [Id] INTEGER NOT NULL
    ALTER TABLE [SomeTable] ADD CONSTRAINT PK_JobInfo PRIMARY KEY (Id)

    ALTER TABLE [SomeTable] CHECK CONSTRAINT ALL

    COMMIT TRAN
END TRY
BEGIN CATCH
    ROLLBACK TRAN   
    SELECT ERROR_MESSAGE ()
END CATCH
bluedot
  • 628
  • 8
  • 24
5

Bellow code working as fine, when we don't know identity column name.

Need to copy data into new temp table like Invoice_DELETED. and next time we using:

insert into Invoice_DELETED select * from Invoice where ...


SELECT t1.*
INTO Invoice_DELETED
FROM Invoice t1
LEFT JOIN Invoice ON 1 = 0
--WHERE t1.InvoiceID = @InvoiceID

For more explanation see: https://dba.stackexchange.com/a/138345/101038

Zolfaghari
  • 1,259
  • 1
  • 15
  • 14
4

In SQL Server you can turn on and off identity insert like this:

SET IDENTITY_INSERT table_name ON

-- run your queries here

SET IDENTITY_INSERT table_name OFF

Community
  • 1
  • 1
3
ALTER TABLE tablename add newcolumn int
update tablename set newcolumn=existingcolumnname
ALTER TABLE tablename DROP COLUMN existingcolumnname;
EXEC sp_RENAME 'tablename.oldcolumn' , 'newcolumnname', 'COLUMN'

However above code works only if no primary-foreign key relation

Jekin Kalariya
  • 3,475
  • 2
  • 20
  • 32
1

Just for someone who have the same problem I did. If you just want to make some insert just once you can do something like this.

Lets suppose you have a table with two columns

ID Identity (1,1) | Name Varchar

and want to insert a row with the ID = 4. So you Reseed it to 3 so the next one is 4

DBCC CHECKIDENT([YourTable], RESEED, 3)

Make the Insert

INSERT  INTO [YourTable]
        ( Name )
VALUES  ( 'Client' )

And get your seed back to the highest ID, lets suppose is 15

DBCC CHECKIDENT([YourTable], RESEED, 15)

Done!

1

I had the same requirement, and you could try this way, which I personally recommend you, please manually design your table and generate the script, and what I did below was renaming the old table and also its constraint for backup.

/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION

SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.SI_Provider_Profile
    DROP CONSTRAINT DF_SI_Provider_Profile_SIdtDateTimeStamp
GO
ALTER TABLE dbo.SI_Provider_Profile
    DROP CONSTRAINT DF_SI_Provider_Profile_SIbHotelPreLoaded
GO
CREATE TABLE dbo.Tmp_SI_Provider_Profile
    (
    SI_lProvider_Profile_ID int NOT NULL,
    SI_lSerko_Integrator_Token_ID int NOT NULL,
    SI_sSerko_Integrator_Provider varchar(50) NOT NULL,
    SI_sSerko_Integrator_Profile varchar(50) NOT NULL,
    SI_dtDate_Time_Stamp datetime NOT NULL,
    SI_lProvider_ID int NULL,
    SI_sDisplay_Name varchar(10) NULL,
    SI_lPurchased_From int NULL,
    SI_sProvider_UniqueID varchar(255) NULL,
    SI_bHotel_Pre_Loaded bit NOT NULL,
    SI_sSiteName varchar(255) NULL
    )  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile SET (LOCK_ESCALATION = TABLE)
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
    DF_SI_Provider_Profile_SIdtDateTimeStamp DEFAULT (getdate()) FOR SI_dtDate_Time_Stamp
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
    DF_SI_Provider_Profile_SIbHotelPreLoaded DEFAULT ((0)) FOR SI_bHotel_Pre_Loaded
GO
IF EXISTS(SELECT * FROM dbo.SI_Provider_Profile)
        EXEC('INSERT INTO dbo.Tmp_SI_Provider_Profile (SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName)
        SELECT SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName FROM dbo.SI_Provider_Profile WITH (HOLDLOCK TABLOCKX)')
GO

-- Rename the primary key constraint or unique key In SQL Server constraints such as primary keys or foreign keys are objects in their own right, even though they are dependent upon the "containing" table.
EXEC sp_rename 'dbo.SI_Provider_Profile.PK_SI_Provider_Profile', 'PK_SI_Provider_Profile_Old';
GO
-- backup old table in case of 
EXECUTE sp_rename N'dbo.SI_Provider_Profile', N'SI_Provider_Profile_Old', 'OBJECT'
GO

EXECUTE sp_rename N'dbo.Tmp_SI_Provider_Profile', N'SI_Provider_Profile', 'OBJECT'
GO

ALTER TABLE dbo.SI_Provider_Profile ADD CONSTRAINT
    PK_SI_Provider_Profile PRIMARY KEY NONCLUSTERED 
    (
    SI_lProvider_Profile_ID
    ) WITH( PAD_INDEX = OFF, FILLFACTOR = 90, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
COMMIT TRANSACTION
Fedor
  • 1,548
  • 3
  • 28
  • 38
Cena Jang
  • 11
  • 2
0

ALTER TABLE TableName ALTER COLUMN columnName DROP IDENTITY IF EXISTS;

  • Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? **If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient.** Can you kindly [edit] your answer to offer an explanation? – Jeremy Caney Aug 03 '23 at 00:25
-1
ALTER TABLE TABLE_NAME MODIFY (COLUMN_NAME DROP IDENTITY);
4b0
  • 21,981
  • 30
  • 95
  • 142
Sidd
  • 25
  • 1