Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como adicionar uma coluna de identidade a uma tabela de banco de dados existente que possui um grande número de linhas


O processo geral provavelmente será muito mais lento com mais sobrecarga geral de bloqueio, mas se você se importa apenas com o tamanho do log de transações, tente o seguinte.
  1. Adicione uma coluna sem identidade de número inteiro anulável (somente alteração de metadados).
  2. Escreva o código para atualizar isso com números inteiros sequenciais exclusivos em lotes. Isso reduzirá o tamanho de cada transação individual e manterá o tamanho do log baixo (assumindo um modelo de recuperação simples). Meu código abaixo faz isso em lotes de 100, espero que você tenha um PK existente que possa aproveitar para continuar de onde parou, em vez das varreduras repetidas que levarão cada vez mais tempo no final.
  3. use ALTER TABLE ... ALTER COLUMN para marcar a coluna como NOT NULL . Isso exigirá que toda a tabela seja bloqueada e verificada para validar a alteração, mas não exigirá muito registro.
  4. Use ALTER TABLE ... SWITCH para tornar a coluna uma coluna de identidade. Esta é uma alteração apenas de metadados.

Código de exemplo abaixo
/*Set up test table with just one column*/

CREATE TABLE table_1 ( original_column INT )
INSERT  INTO table_1
        SELECT DISTINCT
                number
        FROM    master..spt_values



/*Step 1 */
ALTER TABLE table_1 ADD id INT NULL



/*Step 2 */
DECLARE @Counter INT = 0 ,
    @PrevCounter INT = -1

WHILE @PrevCounter <> @Counter 
    BEGIN
        SET @PrevCounter = @Counter;
        WITH    T AS ( SELECT TOP 100
                                * ,
                                ROW_NUMBER() OVER ( ORDER BY @@SPID )
                                + @Counter AS new_id
                       FROM     table_1
                       WHERE    id IS NULL
                     )
            UPDATE  T
            SET     id = new_id
        SET @Counter = @Counter + @@ROWCOUNT
    END


BEGIN TRY;
    BEGIN TRANSACTION ;
     /*Step 3 */
    ALTER TABLE table_1 ALTER COLUMN id INT NOT NULL

    /*Step 4 */
    DECLARE @TableScript NVARCHAR(MAX) = '
    CREATE TABLE dbo.Destination(
        original_column INT,
        id INT IDENTITY(' + CAST(@Counter + 1 AS VARCHAR) + ',1)
        )

        ALTER TABLE dbo.table_1 SWITCH TO dbo.Destination;
    '       

    EXEC(@TableScript)


    DROP TABLE table_1 ;

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


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