Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

So fügen Sie einer vorhandenen Datenbanktabelle mit einer großen Anzahl von Zeilen eine Identitätsspalte hinzu

Der Gesamtprozess wird wahrscheinlich viel langsamer sein, mit mehr Gesamtsperraufwand, aber wenn Sie sich nur um die Größe des Transaktionsprotokolls kümmern, können Sie Folgendes versuchen.

  1. Fügen Sie eine Nullable-Integer-Nicht-Identitätsspalte hinzu (nur Metadaten ändern sich).
  2. Schreiben Sie Code, um dies mit eindeutigen sequenziellen Ganzzahlen in Stapeln zu aktualisieren. Dadurch wird die Größe jeder einzelnen Transaktion reduziert und die Protokollgröße niedrig gehalten (unter der Annahme eines einfachen Wiederherstellungsmodells). Mein Code unten tut dies in Stapeln von 100. Hoffentlich haben Sie einen vorhandenen PK, den Sie nutzen können, um dort weiterzumachen, wo Sie aufgehört haben, anstatt die wiederholten Scans, die gegen Ende immer länger dauern.
  3. benutze ALTER TABLE ... ALTER COLUMN um die Spalte als NOT NULL zu markieren . Dazu muss die gesamte Tabelle gesperrt und gescannt werden, um die Änderung zu validieren, aber es ist nicht viel Protokollierung erforderlich.
  4. Verwenden Sie ALTER TABLE ... SWITCH um die Spalte zu einer Identitätsspalte zu machen. Dies ist nur eine Änderung der Metadaten.

Beispielcode unten

/*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 ;