Adding an identity to an existing column

后端 未结 19 2069
温柔的废话
温柔的废话 2020-11-21 13:16

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

相关标签:
19条回答
  • 2020-11-21 13:44

    Consider to use SEQUENCE instead of IDENTITY.

    IN sql server 2014 (I don't know about lower versions) you can do this simply, using sequence.

    CREATE SEQUENCE  sequence_name START WITH here_higher_number_than_max_existed_value_in_column INCREMENT BY 1;
    
    ALTER TABLE table_name ADD CONSTRAINT constraint_name DEFAULT NEXT VALUE FOR sequence_name FOR column_name
    

    From here: Sequence as default value for a column

    0 讨论(0)
  • 2020-11-21 13:44

    If the original poster was actually wanting to set an existing column to be a PRIMARY KEY for the table and actually did not need the column to be an IDENTITY column (two different things) then this can be done via t-SQL with:

    ALTER TABLE [YourTableName]
    ADD CONSTRAINT [ColumnToSetAsPrimaryKey] PRIMARY KEY ([ColumnToSetAsPrimaryKey])
    

    Note the parenthesis around the column name after the PRIMARY KEY option.

    Although this post is old and I am making an assumption about the requestors need, I felt this additional information could be helpful to users encountering this thread as I believe the conversation could lead one to believe that an existing column can not be set to be a primary key without adding it as a new column first which would be incorrect.

    0 讨论(0)
  • 2020-11-21 13:45

    You cannot alter a column to be an IDENTITY column. What you'll need to do is create a new column which is defined as an IDENTITY from the get-go, then drop the old column, and rename the new one to the old name.

    ALTER TABLE (yourTable) ADD NewColumn INT IDENTITY(1,1)
    
    ALTER TABLE (yourTable) DROP COLUMN OldColumnName
    
    EXEC sp_rename 'yourTable.NewColumn', 'OldColumnName', 'COLUMN'
    

    Marc

    0 讨论(0)
  • 2020-11-21 13:45

    you can't do it like that, you need to add another column, drop the original column and rename the new column or or create a new table, copy the data in and drop the old table followed by renaming the new table to the old table

    if you use SSMS and set the identity property to ON in the designer here is what SQL Server does behind the scenes. So if you have a table named [user] this is what happens if you make UserID and identity

    BEGIN TRANSACTION
    SET QUOTED_IDENTIFIER ON
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    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
    
    GO
    CREATE TABLE dbo.Tmp_User
        (
        UserID int NOT NULL IDENTITY (1, 1),
        LastName varchar(50) NOT NULL,
        FirstName varchar(50) NOT NULL,
        MiddleInitial char(1) NULL
    
        )  ON [PRIMARY]
    GO
    
    SET IDENTITY_INSERT dbo.Tmp_User ON
    GO
    IF EXISTS(SELECT * FROM dbo.[User])
     EXEC('INSERT INTO dbo.Tmp_User (UserID, LastName, FirstName, MiddleInitial)
        SELECT UserID, LastName, FirstName, MiddleInitialFROM dbo.[User] TABLOCKX')
    GO
    SET IDENTITY_INSERT dbo.Tmp_User OFF
    GO
    
    GO
    DROP TABLE dbo.[User]
    GO
    EXECUTE sp_rename N'dbo.Tmp_User', N'User', 'OBJECT'
    GO
    ALTER TABLE dbo.[User] ADD CONSTRAINT
        PK_User PRIMARY KEY CLUSTERED 
        (
        UserID
        ) ON [PRIMARY]
    
    GO
    COMMIT
    

    Having said that there is a way to hack the system table to accomplish it by setting the bitwise value but that is not supported and I wouldn't do it

    0 讨论(0)
  • 2020-11-21 13:50

    generates a script for all tables with primary key = bigint which do not have an identity set; this will return a list of generated scripts with each table;

    SET NOCOUNT ON;
    
    declare @sql table(s varchar(max), id int identity)
    
    DECLARE @table_name nvarchar(max),
            @table_schema nvarchar(max);
    
    DECLARE vendor_cursor CURSOR FOR 
    SELECT
      t.name, s.name
    FROM sys.schemas AS s
    INNER JOIN sys.tables AS t
      ON s.[schema_id] = t.[schema_id]
    WHERE EXISTS (
        SELECT
        [c].[name]
        from sys.columns [c]
        join sys.types [y] on [y].system_type_id = [c].system_type_id
        where [c].[object_id] = [t].[object_id] and [y].name = 'bigint' and [c].[column_id] = 1
    ) and NOT EXISTS 
    (
      SELECT 1 FROM sys.identity_columns
        WHERE [object_id] = t.[object_id]
    ) and exists (
        select 1 from sys.indexes as [i] 
        inner join sys.index_columns as [ic]  ON  i.OBJECT_ID = ic.OBJECT_ID AND i.index_id = ic.index_id
        where object_name([ic].[object_id]) = [t].[name]
    )
    OPEN vendor_cursor
    
    FETCH NEXT FROM vendor_cursor 
    INTO @table_name, @table_schema
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
    
    DELETE FROM @sql
    
    declare @pkname varchar(100),
        @pkcol nvarchar(100)
    
    SELECT  top 1
            @pkname = i.name,
            @pkcol = COL_NAME(ic.OBJECT_ID,ic.column_id)
    FROM    sys.indexes AS [i]
    INNER JOIN sys.index_columns AS [ic] ON  i.OBJECT_ID = ic.OBJECT_ID AND i.index_id = ic.index_id
    WHERE   i.is_primary_key = 1 and OBJECT_NAME(ic.OBJECT_ID) = @table_name
    
    declare @q nvarchar(max) = 'SELECT  '+@pkcol+' FROM ['+@table_schema+'].['+@table_name+'] ORDER BY '+@pkcol+' DESC'
    
    DECLARE @ident_seed nvarchar(max) -- Change this to the datatype that you are after
    SET @q = REPLACE(@q, 'SELECT', 'SELECT TOP 1 @output = ')
    EXEC sp_executeSql @q, N'@output bigint OUTPUT', @ident_seed OUTPUT
    
    insert into  @sql(s) values ('BEGIN TRANSACTION')
    insert into  @sql(s) values ('BEGIN TRY')
    
    -- create statement
    insert into  @sql(s) values ('create table ['+@table_schema+'].[' + @table_name + '_Temp] (')
    
    -- column list
    insert into @sql(s) 
    select 
        '  ['+[c].[name]+'] ' +
        y.name + 
    
        (case when [y].[name] like '%varchar' then
        coalesce('('+(case when ([c].[max_length] < 0 or [c].[max_length] >= 1024) then 'max' else cast([c].max_length as varchar) end)+')','')
        else '' end)
    
         + ' ' +
        case when [c].name = @pkcol then 'IDENTITY(' +COALESCE(@ident_seed, '1')+',1)' else '' end + ' ' +
        ( case when c.is_nullable = 0 then 'NOT ' else '' end ) + 'NULL ' + 
        coalesce('DEFAULT ('+(
            REPLACE(
                REPLACE(
                    LTrim(
                        RTrim(
                            REPLACE(
                                REPLACE(
                                    REPLACE(
                                        REPLACE(
                                            LTrim(
                                                RTrim(
                                                    REPLACE(
                                                        REPLACE(
                                                            object_definition([c].default_object_id)
                                                        ,' ','~')
                                                    ,')',' ')
                                                )
                                            )
                                        ,' ','*')
                                    ,'~',' ')
                                ,' ','~')
                            ,'(',' ')
                        )
                    )
                ,' ','*')
            ,'~',' ')
        ) +
        case when object_definition([c].default_object_id) like '%get%date%' then '()' else '' end
        +
        ')','') + ','
     from sys.columns c
     JOIN sys.types y ON y.system_type_id = c.system_type_id
      where OBJECT_NAME(c.[object_id]) = @table_name and [y].name != 'sysname'
     order by [c].column_id
    
    
     update @sql set s=left(s,len(s)-1) where id=@@identity
    
    -- closing bracket
    insert into @sql(s) values( ')' )
    
    insert into @sql(s) values( 'SET IDENTITY_INSERT ['+@table_schema+'].['+@table_name+'_Temp] ON')
    
    declare @cols nvarchar(max)
    SELECT @cols = STUFF(
        (
            select ',['+c.name+']'
            from sys.columns c
            JOIN sys.types y ON y.system_type_id = c.system_type_id
            where c.[object_id] = OBJECT_ID(@table_name)
            and [y].name != 'sysname'
            and [y].name != 'timestamp'
            order by [c].column_id
            FOR XML PATH ('')
         )
        , 1, 1, '')
    
    insert into @sql(s) values( 'IF EXISTS(SELECT * FROM ['+@table_schema+'].['+@table_name+'])')
    insert into @sql(s) values( 'EXEC(''INSERT INTO ['+@table_schema+'].['+@table_name+'_Temp] ('+@cols+')')
    insert into @sql(s) values( 'SELECT '+@cols+' FROM ['+@table_schema+'].['+@table_name+']'')')
    
    insert into @sql(s) values( 'SET IDENTITY_INSERT ['+@table_schema+'].['+@table_name+'_Temp] OFF')
    
    
    insert into @sql(s) values( 'DROP TABLE ['+@table_schema+'].['+@table_name+']')
    
    insert into @sql(s) values( 'EXECUTE sp_rename N''['+@table_schema+'].['+@table_name+'_Temp]'', N'''+@table_name+''', ''OBJECT''')
    
    if ( @pkname is not null ) begin
        insert into @sql(s) values('ALTER TABLE ['+@table_schema+'].['+@table_name+'] ADD CONSTRAINT ['+@pkname+'] PRIMARY KEY CLUSTERED (')
        insert into @sql(s)
            select '  ['+COLUMN_NAME+'] ASC,' from information_schema.key_column_usage
            where constraint_name = @pkname
            GROUP BY COLUMN_NAME, ordinal_position
            order by ordinal_position
    
        -- remove trailing comma
        update @sql set s=left(s,len(s)-1) where id=@@identity
        insert into @sql(s) values ('  )')
    end
    
    insert into  @sql(s) values ('--Run your Statements')
    insert into  @sql(s) values ('COMMIT TRANSACTION')
    insert into  @sql(s) values ('END TRY')
    insert into  @sql(s) values ('BEGIN CATCH')
    insert into  @sql(s) values ('        ROLLBACK TRANSACTION')
    insert into  @sql(s) values ('        DECLARE @Msg NVARCHAR(MAX)  ')
    insert into  @sql(s) values ('        SELECT @Msg=ERROR_MESSAGE() ')
    insert into  @sql(s) values ('        RAISERROR(''Error Occured: %s'', 20, 101,@msg) WITH LOG')
    insert into  @sql(s) values ('END CATCH')
    
    declare @fqry nvarchar(max)
    
    -- result!
    SELECT @fqry = (select char(10) + s from @sql order by id FOR XML PATH (''))
    
    
    SELECT @table_name as [Table_Name], @fqry as [Generated_Query]
    PRINT 'Table: '+@table_name
    EXEC sp_executeSql @fqry
    
        FETCH NEXT FROM vendor_cursor 
        INTO @table_name, @table_schema
    END 
    CLOSE vendor_cursor;
    DEALLOCATE vendor_cursor;
    
    0 讨论(0)
  • 2020-11-21 13:51

    I'm a java developer that happened to get on a team without a DBA and one where as a developer, I can't get DBA rights. I was tasked with moving an entire schema between two databases, so without having a DBA, I had to do it and do it by running scripts, not being able to use the GUI in SQL Server 2008 because I didn't have admin privileges.

    Everything was moved without issue, however, when running a stored procedure on the new schema.table, I found I lost the identity field in a table. I double checked the script that created the table and it was there, however, SQL Server didn't get it when I ran the script. I was told later by a DBA that he had seen this same problem before.

    In any event, for SQL Server 2008, these are the steps I took to get this resolved and they worked, so I'm posting this here in the hopes it will be a help to someone. This is what I did as I had FK dependencies on another table that made this more difficult:

    I used this query to verify the identity was indeed missing and to view dependencies on the table.

    1.) Find statistics on a table:

    exec sp_help 'dbo.table_name_old';
    

    2.) Create a duplicate, identical new table, except add an identity field on the PK field where it had been before.

    3.) Disable the identity to move data.

    SET IDENTITY_INSERT dbo.table_name ON 
    

    4.) Transfer the data.

    INSERT INTO dbo.table_name_new
    (
    field1, field2, etc...
    )
    SELECT 
    field1, field2, etc...
    FROM 
    dbo.table_name_old;
    

    5.) Verify the data is there.

    SELECT * FROM dbo.table_name_new
    

    6.) Re-enable the identity.

    SET IDENTITY_INSERT ToyRecP.ToyAwards.lkpFile_New OFF
    

    7.) This is the best script I found to get all the FK relationships to verify which table(s) the original table references as dependencies and I came across many, so it is a keeper!

    SELECT f.name AS ForeignKey,
       OBJECT_NAME(f.parent_object_id) AS TableName,
       COL_NAME(fc.parent_object_id, fc.parent_column_id) AS ColumnName,
       OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName,
       COL_NAME(fc.referenced_object_id, fc.referenced_column_id) AS ReferenceColumnName
    FROM sys.foreign_keys AS f
    INNER JOIN sys.foreign_key_columns AS fc
       ON f.OBJECT_ID = fc.constraint_object_id
       ORDER BY ReferenceTableName;
    

    8.) Make sure you have all the PK and FK scripts for all the tables involved, before this next step.

    9.) You can right-click on each key and script this using SQL Server 2008

    10.) Drop the FK(s) from the dependency table(s) using this syntax:

    ALTER TABLE [dbo].[table_name] DROP CONSTRAINT [Name_of_FK]
    

    11.) Drop the original table:

    DROP TABLE dbo.table_name_old;
    

    13.) These next steps rely on the scripts you created in SQL Server 2008 in step 9.

    --Add the PK to the new table.

    --Add the FK to the new table.

    --Add the FK's back to the dependency table.

    14.) Verify everything is correct and complete. I used the GUI to look at the tables.

    15.) Rename the new table to the original tables name.

    exec sp_RENAME '[Schema_Name.OldTableName]' , '[NewTableName]';
    

    Finally, everything worked!

    0 讨论(0)
提交回复
热议问题