Comment migrer entre des schémas en bloc dans SQL Server?

8

Nous avons actuellement plusieurs bases de données, mais nous aimerions les combiner et, au lieu de cela, séparer nos contextes de domaine à l'aide de schémas.

Dans MS SQL Server 2008 R2, comment déplacer en bloc tout le contenu d'un schéma dans un autre?

Par exemple, toutes les tables, vues, procédures, index, etc. que nous avons créées dans le dboschéma vivront désormais dans le fooschéma.

EDIT: Je voulais clarifier sur la base des excellents commentaires d'AaronBertrand. Ce n'est pas une situation de location multiple. Notre situation est celle où les plugins d'outils internes ont été développés de manière isolée par des développeurs qui n'ont pas fusionné leurs tables dans la base de données de l'outil.

Matthieu
la source
2
Cela pourrait devenir très délicat avec FK et d'autres dépendances. Pourquoi changez-vous de modèle? J'aime le modèle multi-db par rapport au modèle multi-schéma pour diverses raisons - voir dba.stackexchange.com/questions/33550/… et dba.stackexchange.com/questions/16745/…
Aaron Bertrand
@AaronBertrand parmi les raisons est que nous avons des dépendances relationnelles entre les entités qui se trouvent dans des bases de données distinctes (les utilisateurs, par exemple, dans leur propre base de données) et, également, que nous avons des vues qui s'étendent sur les bases de données et ne peuvent donc pas utiliser la liaison de schémas.
Matthew
Vous n'avez pas besoin de clés étrangères pour avoir des relations - celles-ci peuvent être appliquées explicitement de diverses autres manières. Et vos vues n'ont pas besoin d'avoir SCHEMABINDING sauf si elles sont indexées. J'ai travaillé avec un tel système pendant 13 ans, et je peux vous promettre que les choses dont vous vous inquiétez ne valent pas la peine de vous inquiéter par rapport à celles que vous perdrez en combinant tout le monde dans une seule base de données.
Aaron Bertrand
@AaronBertrand la grande différence que je vois entre mon cas d'utilisation et celui souvent décrit dans votre lien est que ces bases de données ne séparent en aucun cas les clients ou les locataires. Chacun d'eux est utilisé par une combinaison d'outils internes. La raison pour laquelle ils sont séparés est probablement parce que le développeur crée le plugin d'outil dans le vide puis l'a attaché sans fusionner dans la base de données principale. En fait, nous aurons des bases de données distinctes pour gérer la location et les différents environnements ...
Matthew

Réponses:

9

Le concept de base est en fait assez simple: vous générez un script à partir de sys.objectset sys.schemasqui construit des ALTER SCHEMA TRANSFERinstructions. Ainsi, par exemple, vous avez trois objets dans le dboschéma et vous souhaitez tous les déplacer vers le blatschéma:

Table: dbo.foo
Table: dbo.bar
View:  dbo.vFooBar

Le code suivant:

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += N'
  ALTER SCHEMA blat TRANSFER dbo.' + QUOTENAME(o.name) + ';'
FROM sys.objects AS o
INNER JOIN sys.schemas AS s
ON o.[schema_id] = s.[schema_id]
WHERE s.name = N'dbo';

PRINT @sql;
-- EXEC sp_executesql @sql;

Donnera ce script (mais peut-être pas dans cet ordre):

ALTER SCHEMA blat TRANSFER dbo.bar;
ALTER SCHEMA blat TRANSFER dbo.foo;
ALTER SCHEMA blat TRANSFER dbo.vFooBar;

(Vous souhaiterez peut-être ajouter des filtres supplémentaires pour exclure les objets du dboschéma que vous ne souhaitez pas déplacer, exclure certains types d'objet (par exemple, toutes vos fonctions sont des fonctions utilitaires et n'ont pas besoin de se déplacer), générez le script ordonné par type d'objet, etc.)

Mais il y a quelques problèmes avec le déplacement de tous vos objets vers un nouveau schéma:

  1. Probablement, votre code fera toujours référence à ces objets dbo.object- il n'y a pas de moyen facile de résoudre ce problème, sauf la force brute. Vous pouvez probablement trouver toutes les occurrences de dbo.assez facilement, mais celles-ci peuvent également renvoyer des faux positifs, tels que EXEC dbo.sp_executesql, dbo.dans les commentaires, de vraies références à des objets qui restent dans le dbo.schéma, etc.

  2. Vos dépendances seront probablement complètement hors de contrôle, mais je n'ai pas testé cela à fond. Je sais que dans ce scénario:

    CREATE SCHEMA blat AUTHORIZATION dbo;
    GO
    
    CREATE TABLE dbo.foo(a INT PRIMARY KEY);
    CREATE TABLE dbo.bar(a INT FOREIGN KEY REFERENCES dbo.foo(a));
    GO
    
    CREATE PROCEDURE dbo.pX AS
    BEGIN
      SET NOCOUNT ON;
      SELECT a FROM dbo.bar;
    END
    GO
    
    CREATE VIEW dbo.vFooBar
    AS
      SELECT foo.a, bar.a AS barA
        FROM dbo.foo 
        INNER JOIN dbo.bar
        ON foo.a = bar.a;
    GO
    
    ALTER SCHEMA blat TRANSFER dbo.foo;
    ALTER SCHEMA blat TRANSFER dbo.bar;
    ALTER SCHEMA blat TRANSFER dbo.pX;
    ALTER SCHEMA blat TRANSFER dbo.vFooBar;
    

    Les clés étrangères migrent en fait plus facilement que prévu (avec la mise en garde que je teste sur une version beaucoup plus récente que vous). Mais parce que le code fait blat.pXtoujours référence dbo.bar, exécutant évidemment la procédure:

    EXEC blat.pX;

    Va produire cette erreur:

    Msg 208, niveau 16, état 1, procédure pX
    Nom d'objet non valide 'dbo.bar'.

    Et les requêtes de dépendance, telles que:

    SELECT * FROM sys.dm_sql_referenced_entities('blat.pX', N'OBJECT');

    Donnera cette erreur:

    Msg 2020, niveau 16, état 1
    Les dépendances signalées pour l'entité "blat.pX" peuvent ne pas inclure de références à toutes les colonnes. Cela est dû au fait que l'entité fait référence à un objet qui n'existe pas ou à cause d'une erreur dans une ou plusieurs instructions de l'entité. Avant de réexécuter la requête, assurez-vous qu'il n'y a pas d'erreur dans l'entité et que tous les objets référencés par l'entité existent.

    Et interroger la vue:

    SELECT a, barA FROM blat.vFooBar;

    Donne ces erreurs:

    Msg 208, niveau 16, état 1, procédure vFooBar
    Nom d'objet non valide 'dbo.foo'.
    Msg 4413, niveau 16, état 1
    Impossible d'utiliser la vue ou la fonction «blat.vFoobar» en raison d'erreurs de liaison.

    Donc, cela pourrait impliquer beaucoup de nettoyage. Et après avoir corrigé toutes les références d'objet, vous souhaiterez probablement recompiler tous les modules et actualiser toutes les vues dans le nouveau schéma. Vous pouvez générer un script pour cela assez similaire à l'exemple ci-dessus.

Aaron Bertrand
la source
+1, il semble que je doive supprimer manuellement certains FK pour effectuer ce transfert ... Je ne sais pas encore ce qui cause cela
Matthew
@Matthew ouais, si les clés étrangères vous posent problème, voyez cette réponse - vous aurez juste besoin de l'ajuster pour que la partie recréée du script fasse référence au nouveau schéma.
Aaron Bertrand
@Matthew, avez-vous compris les circonstances qui empêchent certaines tables avec des clés étrangères de se transférer en douceur? Quel est le message d'erreur exact que vous obtenez? Il serait intéressant de savoir si vous pouvez contourner cela en désactivant simplement la contrainte plutôt qu'en la supprimant ... mais difficile pour moi de tester car je ne sais pas quel est votre bloqueur exact.
Aaron Bertrand