Como migrar entre esquemas em massa no SQL Server?

8

Atualmente, temos vários bancos de dados, mas gostaríamos de combiná-los e, em vez disso, separar nossos contextos de domínio usando esquemas.

No MS SQL Server 2008 R2, como posso realocar todo o conteúdo de um esquema para outro em massa?

Por exemplo, todas as tabelas, visualizações, procedimentos, índices, etc ... que criamos no dboesquema agora estarão no fooesquema.

EDIT: Eu queria esclarecer com base nos ótimos comentários de AaronBertrand. Esta não é uma situação de multilocação. Nossa situação é onde os plug-ins de ferramentas internas foram desenvolvidos isoladamente por desenvolvedores que não mesclaram suas tabelas no banco de dados da ferramenta.

Mateus
fonte
2
Isso pode ser realmente complicado com o FK e outras dependências. Por que você está trocando de modelo? Eu gosto do modelo multi-db sobre o modelo multi-schema por vários motivos - consulte dba.stackexchange.com/questions/33550/… e dba.stackexchange.com/questions/16745/…
Aaron Bertrand
O @AaronBertrand entre os motivos é que temos dependências relacionais entre entidades que estão em bancos de dados separados (usuários, por exemplo, em seu próprio banco de dados) e também que temos visualizações que abrangem bancos de dados e, portanto, não podem usar vinculação de esquema.
Mateus
Você não precisa de chaves estrangeiras para ter relacionamentos - elas podem ser aplicadas explicitamente de várias outras maneiras. E suas visualizações não precisam ter SCHEMABINDING, a menos que sejam indexadas. Trabalhei com esse sistema por 13 anos e posso prometer que não vale a pena se preocupar com as coisas com as quais você se preocupa em comparação com as coisas que você perderá ao combinar todos em um único banco de dados.
Aaron Bertrand
@AaronBertrand, a grande diferença que vejo entre meu caso de uso e o descrito frequentemente em seu link é que esses bancos de dados não separam clientes ou inquilinos de nenhuma maneira. Cada um deles é usado por uma combinação de ferramentas internas. Provavelmente, a razão pela qual eles são separados é porque o desenvolvedor cria o plug-in de ferramenta no vácuo e o anexa sem mesclar no banco de dados principal. Nós realmente vai ter DB separada para lidar com ambientes de arrendamento e diferentes ...
Matthew

Respostas:

9

O conceito básico é realmente bastante simples: você gera um script sys.objectse sys.schemasconstrói ALTER SCHEMA TRANSFERinstruções. Por exemplo, você tem três objetos no dboesquema e deseja mover todos eles para oblat esquema:

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

O código a seguir:

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;

Produzirá este script (mas talvez não nesta ordem):

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

Você pode adicionar filtros adicionais para deixar de fora os objetos no dbo esquema que não deseja mover, excluir certos tipos de objetos (por exemplo, talvez todas as suas funções sejam funções utilitárias e não precisem ser movidas), gere o script ordenado por tipo de objeto etc.)

Mas existem alguns problemas ao mover todos os seus objetos para um novo esquema:

  1. Provavelmente, seu código ainda fará referência a esses objetos como dbo.object- não há maneira fácil de corrigir isso, exceto a força bruta. Provavelmente, você pode encontrar todas as ocorrências com dbo.bastante facilidade, mas elas também podem retornar falsos positivos, como EXEC dbo.sp_executesql,dbo. nos comentários, verdadeiras referências a objetos que permanecem no dbo.esquema, etc.

  2. Suas dependências provavelmente estarão completamente fora de sintonia, mas ainda não testei isso completamente. Eu sei que neste cenário:

    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;

    As chaves estrangeiras realmente migram mais suavemente do que eu esperava (com a ressalva de que estou testando em uma versão muito mais recente que você). Mas porque o código blat.pXainda faz referênciadbo.bar , obviamente executando o procedimento:

    EXEC blat.pX;

    Vai gerar este erro:

    Msg 208, nível 16, estado 1, procedimento pX
    Nome de objeto inválido 'dbo.bar'.

    E consultas de dependência, como:

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

    Irá gerar este erro:

    Msg 2020, Nível 16, Estado 1
    As dependências relatadas para a entidade "blat.pX" podem não incluir referências a todas as colunas. Isso ocorre porque a entidade faz referência a um objeto que não existe ou devido a um erro em uma ou mais instruções na entidade. Antes de executar novamente a consulta, verifique se não há erros na entidade e se todos os objetos referenciados pela entidade.

    E consultando a visualização:

    SELECT a, barA FROM blat.vFooBar;

    Rende estes erros:

    Msg 208, Nível 16, Estado 1, Procedimento vFooBar
    Nome de objeto inválido 'dbo.foo'.
    Msg 4413, Nível 16, Estado 1
    Não foi possível usar a exibição ou a função 'blat.vFoobar' devido a erros de ligação.

    Portanto, isso pode envolver muita limpeza. E depois de corrigir todas as referências a objetos, você provavelmente desejará recompilar todos os módulos e atualizar todas as visualizações no novo esquema. Você pode gerar um script parecido com o exemplo acima.

Aaron Bertrand
fonte
+1, parece que eu preciso soltar manualmente alguns FKs para fazer essa transferência ... Ainda não sei o que causa isso #
Matthew
@ Matthew yeah, se chaves estrangeiras estão causando problemas, veja esta resposta - você só precisará ajustá-lo para que a parte recriada do script faça referência ao novo esquema.
Aaron Bertrand
@ Matthew, você já descobriu que circunstâncias existem que impedem que determinadas tabelas com chaves estrangeiras sejam transferidas sem problemas? Qual é a mensagem de erro exata que você recebe? Seria interessante saber se você pode ignorar isso simplesmente desativando a restrição em vez de eliminá-la ... mas difícil para eu testar, pois não sei qual é o seu bloqueador exato.
Aaron Bertrand