web-dev-qa-db-ja.com

列のデータ型の変更を含むSSDTによるデータベース変更の公開

ビルドプロセス中に自動的に公開されるSQL Serverデータツール(VS2012)プロジェクトがあります。最近、列がintからdecimal(18,4)に更新されました。この変更の結果、発行はエラーで失敗します

(49,1):SQL72014:.Net SqlClientデータプロバイダー:メッセージ50000、レベル16、状態127、行6行が検出されました。データ損失が発生する可能性があるため、スキーマの更新を終了しています。 (44,0):SQL72045:スクリプト実行エラー。実行されたスクリプト:/ *テーブル[dbo]。[Reconciliation_Receiving]の列QuantityReceivedのタイプは現在INT NOT NULLですが、DECIMAL(18、4)NOT NULLに変更されています。データ損失が発生する可能性があります。 * /

IF EXISTS([dbo]。[Reconciliation_Receiving]から上位1 1を選択します)RAISERROR(N'Rows was detected。実行されています。

私は理解しています理由そのエラーを受信して​​いますが、[データ損失が発生した場合に増分展開をブロックする]フラグを無効にすることで解決できることを知っています。ただし、その機能を無効にすることには非常に強い反対があるため、これは許容できる解決策にはなりません。

私が考えることができる他の唯一の解決策は次のことをすることです:

  1. 一時テーブルを作成し、既存のテーブルの内容を一時テーブルにコピーします
  2. 既存のテーブルを切り捨てます
  3. SSDTにデータ型を更新させる
  4. 一時テーブルからデータを再入力します

しかし、それは恐ろしく不格好で非効率的です。

より良い代替案はありますか?

9
Daniel Mann

私もそのフラグをバイパスするように誘惑されましたが、同僚の側に降りてきて、これらの問題に「正しく」対処しようとしています。 (少し)扱いにくいルートは、展開前および展開後のスクリプトを使用して、名前変更の作業を行うことです。

  • 配置前スクリプトで既存のテーブルの名前を変更します。
  • 既存のテーブルがない場合、フォーカスされているテーブルは、新しいスキーマ定義に従って作成されます。
  • 展開後のスクリプトで、名前を変更した元のテーブルから新しいバージョンにコピーします。

ターゲットの性質によっては、もちろん、外部キー制約の削除と再作成に注意する必要があります。

4

私の場合、テーブルから列を削除していました。

これで提供されたソリューション answer は私にとってうまくいきませんでした、それはinvalid object name公開中のエラー。

テーブルから行をコピーし、制約チェックを無効にして、配置前スクリプトで行を削除し、配置後スクリプトでID挿入を有効にして、行をテーブルにコピーする必要があることがわかりました。

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

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
2
Alex