web-dev-qa-db-fra.com

Publication des modifications de base de données avec SSDT qui incluent la modification des types de données des colonnes

J'ai un projet SQL Server Data Tools (VS2012) qui est publié automatiquement pendant le processus de génération. Une colonne a été récemment mise à jour de int vers decimal(18,4). À la suite de cette modification, la publication échoue avec l'erreur

(49,1): SQL72014: fournisseur de données .Net SqlClient: Msg 50000, niveau 16, état 127, ligne 6 Des lignes ont été détectées. La mise à jour du schéma se termine car une perte de données peut se produire. (44,0): SQL72045: Erreur d'exécution de script. Le script exécuté:/* Le type de la colonne QuantityReceived dans la table [dbo]. [Reconciliation_Receiving] est actuellement INT NOT NULL mais est changé en DECIMAL (18, 4) NOT NULL. Une perte de données pourrait se produire. * /

SI EXISTE (sélectionnez le top 1 1 dans [dbo]. [Reconciliation_Receiving]) RAISERROR (aucune ligne n'a été détectée. La mise à jour du schéma se termine car une perte de données peut se produire. ', 16, 127) WITH NOWAIT Une erreur s'est produite lors de la génération du lot en cours d'exécution.

Je comprends pourquoi Je reçois cette erreur et je sais qu'elle pourrait être résolue en désactivant l'indicateur "Bloquer le déploiement incrémentiel en cas de perte de données". Cependant, il y a une forte opposition à la désactivation de cette fonctionnalité, donc ce ne sera pas une solution acceptable.

La seule autre solution à laquelle je peux penser est la suivante:

  1. Créer une table temporaire et copier le contenu de la table existante dans la table temporaire
  2. Tronquer la table existante
  3. Laissez SSDT mettre à jour le type de données
  4. Remplissez les données depuis la table temporaire

Cela semble cependant horriblement maladroit et inefficace.

Y a-t-il une meilleure alternative?

9
Daniel Mann

J'ai été tenté de contourner ce drapeau également, mais je suis tombé du côté de vos collègues et essayez maintenant de traiter ces problèmes "correctement". La voie (légèrement) moins maladroite consiste à utiliser des scripts de pré et post-déploiement pour effectuer le travail avec un changement de nom.

  • Renommez la table existante dans un script de pré-déploiement.
  • Avec la table existante manquante, la table en focus sera créée selon la nouvelle définition de schéma.
  • Dans une copie de script post-déploiement de la table d'origine renommée vers la nouvelle version.

En fonction de la nature de la cible, vous devrez bien sûr prendre soin de supprimer et de recréer les contraintes de clé étrangère.

4
Mark Storey-Smith

Dans mon cas, je supprimais une colonne d'une table.

La solution fournie dans cette réponse n'a pas fonctionné pour moi, elle a abouti à un invalid object name erreur lors de la publication.

J'ai constaté qu'il était nécessaire de copier les lignes de la table, de désactiver la vérification des contraintes et de supprimer les lignes dans un script de pré-déploiement, puis de recopier les lignes dans la table avec l'insertion d'identité activée dans le script de post-déploiement.

Dans 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

Dans 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