Veröffentlichen von Datenbankänderungen mit SSDT, einschließlich Ändern von Datentypen von Spalten

9

Ich habe ein SQL Server Data Tools (VS2012) -Projekt, das während des Erstellungsprozesses automatisch veröffentlicht wird. Eine Spalte wurde kürzlich von einem intauf aktualisiert decimal(18,4). Infolge dieser Änderung schlägt die Veröffentlichung mit dem Fehler fehl

(49,1): SQL72014: .Net SqlClient-Datenprovider: Nachricht 50000, Ebene 16, Status 127, Zeile 6 Zeilen wurden erkannt. Die Schemaaktualisierung wird beendet, da möglicherweise Daten verloren gehen. (44,0): SQL72045: Skriptausführungsfehler. Das ausgeführte Skript: / * Der Typ für die Spalte QuantityReceived in Tabelle [dbo]. [Reconciliation_Receiving] ist derzeit INT NOT NULL, wird jedoch in DECIMAL (18, 4) NOT NULL geändert. Datenverlust kann auftreten. * /

WENN EXISTIERT (wählen Sie die oberste 1 1 aus [dbo]. [Reconciliation_Receiving]) RAISERROR (N'Rows wurden erkannt. Die Schemaaktualisierung wird beendet, da möglicherweise Daten verloren gehen. ', 16, 127) WITH NOWAIT Während des Batches ist ein Fehler aufgetreten ausgeführt werden.

Ich verstehe, warum ich diesen Fehler erhalte, und ich weiß, dass er durch Deaktivieren des Flags "Inkrementelle Bereitstellung blockieren, wenn Datenverlust auftreten kann" behoben werden kann. Es gibt jedoch einen starken Widerstand gegen die Deaktivierung dieser Funktion, sodass dies keine akzeptable Lösung sein wird.

Die einzige andere Lösung, die ich mir vorstellen kann, ist Folgendes:

  1. Erstellen Sie eine temporäre Tabelle und kopieren Sie den Inhalt der vorhandenen Tabelle in die temporäre Tabelle
  2. Schneiden Sie die vorhandene Tabelle ab
  3. Lassen Sie SSDT den Datentyp aktualisieren
  4. Füllen Sie die Daten aus der temporären Tabelle wieder aus

Das scheint jedoch schrecklich klobig und ineffizient.

Gibt es eine bessere Alternative?

Daniel Mann
quelle
2
Siehe: Verwalten der Datenbewegung während Ihrer Bereitstellung (Teil 1) ( blogs.msdn.com/b/bahill/archive/2009/03/30/… ). Es behandelt alle Punkte, die @ MarkStorey-Smith anhand von Beispielen beschrieben hat.
Kin Shah

Antworten:

4

Ich war versucht, diese Flagge auch zu umgehen, bin aber auf die Seite Ihrer Mitarbeiter geraten und versuche nun, diese Probleme "richtig" zu behandeln. Die (geringfügig) weniger umständliche Route besteht darin, Skripts vor und nach der Bereitstellung zu verwenden, um die Arbeit mit einer Umbenennung zu erledigen.

  • Benennen Sie die vorhandene Tabelle in einem Skript vor der Bereitstellung um.
  • Wenn die vorhandene Tabelle fehlt, wird die Tabelle im Fokus gemäß der neuen Schemadefinition erstellt.
  • In einem Skript nach der Bereitstellung kopieren Sie die umbenannte Originaltabelle in die neue Version.

Abhängig von der Art des Ziels müssen Sie möglicherweise darauf achten, dass Fremdschlüsseleinschränkungen gelöscht und neu erstellt werden.

Mark Storey-Smith
quelle
Mit "Kopieren von" meinen Sie das Kopieren der Datensätze? Und wenn Sie es umbenannt haben, haben Sie nur 1 Tisch. Wie wird der alte noch da sein? Meinen Sie damit, eine neue Tabelle mit derselben Struktur zu erstellen?
MrFox
@ MrFox Ich werde in den nächsten Tagen ein Beispiel hinzufügen.
Mark Storey-Smith
2

In meinem Fall habe ich eine Spalte aus einer Tabelle entfernt.

Die in dieser Antwort angegebene Lösung hat bei mir nicht funktioniert, sondern zu einem invalid object nameFehler bei der Veröffentlichung geführt.

Ich fand, dass es notwendig war, die Zeilen aus der Tabelle zu kopieren, die Einschränkungsprüfung zu deaktivieren und die Zeilen in einem Skript vor der Bereitstellung zu löschen und die Zeilen dann wieder in die Tabelle zu kopieren, wobei die Identitätseinfügung im Skript nach der Bereitstellung aktiviert war.

In Script.PreDeployment.sql:

-- copy and delete dbo.Table1
BEGIN TRY
    IF (EXISTS (
        SELECT * FROM INFORMATION_SCHEMA.TABLES 
        WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'Table1_copy'))
    BEGIN
        PRINT 'Dropping Table1_copy'
        DROP TABLE dbo.Table1_copy
    END

    PRINT 'Copying dbo.Table1'

    SELECT @LastID = MAX(ID), @StartID = MIN(ID)
    FROM dbo.Table1

    SET @EndID = @StartID + 1000

    SELECT * 
    INTO dbo.Table1_copy 
    FROM dbo.Table1
    WHERE ID BETWEEN @StartID AND @EndId

    SET @StartID = @EndID + 1

    SET IDENTITY_INSERT dbo.Table1_copy ON

    WHILE @StartID < @LastID
    BEGIN
        SET @EndID = @StartID + 1000

        INSERT dbo.Table1_copy (ID, Column1, Column2, Column3)
        SELECT ID, Column1, Column2, Column3
        FROM dbo.Table1
        WHERE ID BETWEEN @StartID AND @EndId

        SET @StartID = @EndID + 1
    END

    SET IDENTITY_INSERT dbo.Table1_copy OFF

    PRINT 'Copied dbo.Table1 to dbo.Table1_copy'

    EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"

    PRINT 'Deleting dbo.Table1'
    WHILE EXISTS (SELECT 1 FROM dbo.Table1) 
        DELETE TOP(1000) FROM dbo.Table1
    PRINT 'Deleted dbo.Table1'

    PRINT 'SUCCESS: Copy and delete dbo.Table1'
END TRY
BEGIN CATCH
    EXEC sp_msforeachtable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"

    PRINT 'ERROR: Copy and delete dbo.Table1'
    PRINT 'ERROR MESSAGE: ' + ERROR_MESSAGE()
END CATCH
GO

In Script.PostDeployment.sql

DECLARE @StartID BIGINT, @LastID BIGINT, @EndID BIGINT

-- populate dbo.Table1
BEGIN TRY
    PRINT 'Populating dbo.Table1'

    SET IDENTITY_INSERT dbo.Table1 ON

    SELECT @LastID = MAX(ID)
    FROM dbo.Table1_copy

    WHILE @StartID < @LastID
    BEGIN
        SET @EndID = @StartID + 1000

        INSERT dbo.Table1 (ID, Column1, Column2, Column3)
        SELECT ID, Column1, Column2, Column3
        FROM dbo.Table1
        WHERE ID BETWEEN @StartID AND @EndId

        SET @StartID = @EndID + 1
    END

    SET IDENTITY_INSERT dbo.Table1 OFF

    EXEC sp_msforeachtable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"

    PRINT 'SUCCESS: Populating dbo.Table1'
END TRY
BEGIN CATCH
    SET IDENTITY_INSERT dbo.Table1 OFF

    PRINT 'ERROR: Populating dbo.Table1'
    PRINT 'ERROR MESSAGE: ' + ERROR_MESSAGE()
END CATCH
GO
Alex
quelle