50

I have a table table1 in SQL server 2008 and it has records in it.

I want the primary key table1_Sno column to be an auto-incrementing column. Can this be done without any data transfer or cloning of table?

I know that I can use ALTER TABLE to add an auto-increment column, but can I simply add the AUTO_INCREMENT option to an existing column that is the primary key?

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
arun
  • 781
  • 2
  • 6
  • 14
  • Not sure you can do this: see http://stackoverflow.com/questions/4862385/sql-server-add-auto-increment-primary-key-to-existing-table – Femi May 21 '11 at 21:43

7 Answers7

66

Changing the IDENTITY property is really a metadata only change. But to update the metadata directly requires starting the instance in single user mode and messing around with some columns in sys.syscolpars and is undocumented/unsupported and not something I would recommend or will give any additional details about.

For people coming across this answer on SQL Server 2012+ by far the easiest way of achieving this result of an auto incrementing column would be to create a SEQUENCE object and set the next value for seq as the column default.

Alternatively, or for previous versions (from 2005 onwards), the workaround posted on this connect item shows a completely supported way of doing this without any need for size of data operations using ALTER TABLE...SWITCH. Also blogged about on MSDN here. Though the code to achieve this is not very simple and there are restrictions - such as the table being changed can't be the target of a foreign key constraint.

Example code.

Set up test table with no identity column.

CREATE TABLE dbo.tblFoo 
(
bar INT PRIMARY KEY,
filler CHAR(8000),
filler2 CHAR(49)
)


INSERT INTO dbo.tblFoo (bar)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM master..spt_values v1, master..spt_values v2

Alter it to have an identity column (more or less instant).

BEGIN TRY;
    BEGIN TRANSACTION;

    /*Using DBCC CHECKIDENT('dbo.tblFoo') is slow so use dynamic SQL to
      set the correct seed in the table definition instead*/
    DECLARE @TableScript nvarchar(max)
    SELECT @TableScript = 
    '
    CREATE TABLE dbo.Destination(
        bar INT IDENTITY(' + 
                     CAST(ISNULL(MAX(bar),0)+1 AS VARCHAR) + ',1)  PRIMARY KEY,
        filler CHAR(8000),
        filler2 CHAR(49)
        )

        ALTER TABLE dbo.tblFoo SWITCH TO dbo.Destination;
    '       
    FROM dbo.tblFoo
    WITH (TABLOCKX,HOLDLOCK)

    EXEC(@TableScript)


    DROP TABLE dbo.tblFoo;

    EXECUTE sp_rename N'dbo.Destination', N'tblFoo', 'OBJECT';


    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
    PRINT ERROR_MESSAGE();
END CATCH;

Test the result.

INSERT INTO dbo.tblFoo (filler,filler2) 
OUTPUT inserted.*
VALUES ('foo','bar')

Gives

bar         filler    filler2
----------- --------- ---------
10001       foo       bar      

Clean up

DROP TABLE dbo.tblFoo
Martin Smith
  • 438,706
  • 87
  • 741
  • 845
  • 1
    +1 Neat trick, I wasn't aware of this workaround. Seems pretty obscure and undocumented, I don't think folks should get down-voted if they didn't know about it. – Aaron Bertrand Jun 19 '12 at 17:23
  • @AaronBertrand - Agree. Wasn't my DV on the other question. – Martin Smith Jun 19 '12 at 17:23
  • Should be noted also that version is important. `SWITCH` won't work pre-2005. I wish tagging `sql-server` also required specifying a minimum version. Not saying that for the benefit of this question, but the one that might get closed as a duplicate of this one. – Aaron Bertrand Jun 19 '12 at 17:29
  • @AaronBertrand - BTW also useful technique for other cases such as altering the [ANSI_NULLS](http://dba.stackexchange.com/a/16230/3690) option or [working around this bug - drop duplicate unique constraint on ROWGUID column on FILESTREAM table](http://connect.microsoft.com/SQLServer/feedback/details/715467/cannot-drop-duplicate-unique-constraint-on-rowguid-column-on-filestream-table) – Martin Smith Jun 19 '12 at 17:33
  • @AaronBertrand - Reversed now whoever it was. Maybe they read the reference to `sys.syscolpars` and thought my answer was going to do that. – Martin Smith Jun 19 '12 at 17:36
  • Doesn't the use of `SWITCH` require that the table already be partitioned? – RBarryYoung Jan 13 '14 at 19:35
  • 1
    @RBarryYoung - No. Every table is regarded as being at least a single partition. This works on all editions of SQL Server. It just changes the `object_id` in `sys.partitions` to point to the new object. – Martin Smith Jan 13 '14 at 19:54
  • This not work if `tblFoo` has it's primary key as a foreign key in another table. – Bernardo Pacheco Jan 16 '15 at 17:51
  • @BernardoPacheco - Agreed this method doesn't work then so you'd need to consider whether it was worth dropping and recreating the FK constraint (meaning the FK would then need to be revalidated on all rows). – Martin Smith Jan 16 '15 at 18:24
5

SQL Server: How to set auto-increment on a table with rows in it:

This strategy physically copies the rows around twice which can take a much longer time if the table you are copying is very large.

You could save out your data, drop and rebuild the table with the auto-increment and primary key, then load the data back in.

I'll walk you through with an example:

Step 1, create table foobar (without primary key or auto-increment):

CREATE TABLE foobar(
    id int NOT NULL,
    name nchar(100) NOT NULL,
)

Step 2, insert some rows

insert into foobar values(1, 'one');
insert into foobar values(2, 'two');
insert into foobar values(3, 'three');

Step 3, copy out foobar data into a temp table:

select * into temp_foobar from foobar

Step 4, drop table foobar:

drop table foobar;

Step 5, recreate your table with the primary key and auto-increment properties:

CREATE TABLE foobar(
    id int primary key IDENTITY(1, 1) NOT NULL,
    name nchar(100) NOT NULL,
)

Step 6, insert your data from temp table back into foobar

SET IDENTITY_INSERT temp_foobar ON
INSERT into foobar (id, name) select id, name from temp_foobar;

Step 7, drop your temp table, and check to see if it worked:

drop table temp_foobar;
select * from foobar;

You should get this, and when you inspect the foobar table, the id column is auto-increment of 1 and id is a primary key:

1    one
2    two
3    three
Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
  • 1
    When I try this I get the error "Table 'temp_foobar' does not have the identity property. Cannot perform SET operation." Am I missing a step? – dallin Aug 08 '14 at 23:54
  • To get this to work, I changed it to "SET IDENTITY_INSERT foobar ON;" as its own statement. In addition, I had to set the NOT FOR REPLICATION option to Yes or I got a "Explicit value must be specified for identity column" error on the INSERT statement. – dallin Aug 09 '14 at 00:07
  • When I'm executing "SET IDENTITY_INSERT temp_foobar ON" I'm still getting the "Table 'temp_foobar' does not have the identity property. Cannot perform SET operation." error – Be Chiller Too Dec 01 '20 at 09:11
3

If you want to do this via the designer you can do it by following the instructions here "Save changes is not permitted" when changing an existing column to be nullable

Community
  • 1
  • 1
Daveo
  • 19,018
  • 10
  • 48
  • 71
2

Yes, you can. Go to Tools > Designers > Table and Designers and uncheck "Prevent Saving Changes That Prevent Table Recreation".

Onik
  • 19,396
  • 14
  • 68
  • 91
1

No, you can not add an auto increment option to an existing column with data, I think the option which you mentioned is the best.

Have a look here.

Homam
  • 23,263
  • 32
  • 111
  • 187
1

If you don't want to add a new column, and you can guarantee that your current int column is unique, you could select all of the data out into a temporary table, drop the table and recreate with the IDENTITY column specified. Then using SET IDENTITY INSERT ON you can insert all of your data in the temporary table into the new table.

Duncan Howe
  • 2,965
  • 19
  • 18
0

Below script can be a good solution.Worked in large data as well.

ALTER DATABASE WMlive SET RECOVERY SIMPLE WITH NO_WAIT

ALTER TABLE WMBOMTABLE DROP CONSTRAINT PK_WMBomTable

ALTER TABLE WMBOMTABLE drop column BOMID

ALTER TABLE WMBOMTABLE ADD BomID int IDENTITY(1, 1) NOT NULL;

ALTER TABLE WMBOMTABLE ADD CONSTRAINT PK_WMBomTable PRIMARY KEY CLUSTERED (BomID);

ALTER DATABASE WMlive SET RECOVERY FULL WITH NO_WAIT

Community
  • 1
  • 1
Jatin Dave
  • 734
  • 1
  • 7
  • 14