如何将标识列添加到具有大量行的现有数据库表

Moh*_*han 6 sql sql-server identity sql-server-2008

我有一个数据库表,有~40 000 000行.我想在此表中添加一个标识列.如何以对数友好的方式做到这一点?

当我执行以下操作时:

ALTER TABLE table_1
  ADD id INT IDENTITY
Run Code Online (Sandbox Code Playgroud)

这只会填满整个日志空间.

有没有办法以对数友好的方式做到这一点?该数据库位于SQL Server 2008上.

谢谢,莫汉.

Mar*_*ith 4

由于整体锁定开销更大,整个过程可能会慢很多,但如果您只关心事务日志大小,您可以尝试以下操作。

  1. 添加可为空整数非标识列(仅元数据更改)。
  2. 编写代码以使用唯一的连续整数批量更新它。这将减少每个单独事务的大小并减少日志大小(假设简单的恢复模型)。我下面的代码以 100 为一组执行此操作,希望您有一个现有的 PK,您可以利用它来从上次停下的地方继续,而不是重复扫描,这样会花费越来越长的时间到最后。
  3. 用于ALTER TABLE ... ALTER COLUMN将该列标记为NOT NULL。这将需要锁定并扫描整个表以验证更改,但不需要太多日志记录。
  4. 用于ALTER TABLE ... SWITCH使该列成为标识列。这只是元数据的更改。

下面的示例代码

/*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 ;
Run Code Online (Sandbox Code Playgroud)