T-SQL "re-executável" scripts de atualização de banco de dados - exclusão de coluna

votos
1

Para o nosso banco de dados SQL Server, estamos usando um sistema de controle de versão para acompanhar as atualizações de esquema. A idéia é que você deve ser capaz de executar este script para trazer o esquema de qualquer versão anterior até a versão atual. Executando o script master novamente só deve executar as atualizações de esquema mais novos.

A estrutura do script é assim:

 SELECT @Installed = InstallDate FROM SystemSchemaVersion WHERE Major=1 AND Minor=0 AND Patch=0
 IF (@Installed IS NULL)
 BEGIN
    ...
    INSERT IGNORE  INTO SystemSchemaVersion (Major, Minor, Patch, InstallDate) VALUES (1, 0, 0, GetDate())
 END
 ELSE PRINT 'Version 1.0.0 was already installed on ' + Convert(varchar(10), @Installed)  

 SELECT @Installed = InstallDate FROM SystemSchemaVersion WHERE Major=1 AND Minor=0 AND Patch=1
 IF (@Installed IS NULL)
 BEGIN
    ...
    INSERT IGNORE  INTO SystemSchemaVersion (Major, Minor, Patch, InstallDate) VALUES (1, 0, 1, GetDate())
 END
 ELSE PRINT 'Version 1.0.1 was already installed on ' + Convert(varchar(10), @Installed)

Isso geralmente funciona muito bem. No entanto, nós encontramos um problema quando uma atualização do esquema deixa cair uma coluna que está incluído em um INSERT anterior IGNORE; ou seja, temos algo parecido com isto:

 SELECT @Installed = InstallDate FROM SystemSchemaVersion WHERE Major=1 AND Minor=0 AND Patch=0
 IF (@Installed IS NULL)
 BEGIN
    INSERT IGNORE  [foo] ([a], [b], [OrganizationId]) VALUES (N'a', N'b', N'1');
    INSERT IGNORE  INTO SystemSchemaVersion (Major, Minor, Patch, InstallDate) VALUES (1, 0, 0, GetDate());
 END
 ELSE PRINT 'Version 1.0.0 was already installed on ' + Convert(varchar(10), @Installed)  

 SELECT @Installed = InstallDate FROM SystemSchemaVersion WHERE Major=1 AND Minor=0 AND Patch=1
 IF (@Installed IS NULL)
 BEGIN
    ALTER TABLE [foo] DROP COLUMN [OrganizationId];
    INSERT IGNORE  INTO SystemSchemaVersion (Major, Minor, Patch, InstallDate) VALUES (1, 0, 1, GetDate());
 END
 ELSE PRINT 'Version 1.0.1 was already installed on ' + Convert(varchar(10), @Installed)

Isso funciona bem na primeira vez que é executado; versão 1.0.1 é executado, ea coluna é descartado. No entanto, a execução do script uma segunda rendimentos tempo:

    Msg 207, nível 16, estado 1, linha 7118
    Nome de coluna inválido 'organizationid'.

Ou seja, mesmo que o INSERT IGNORE dentro do bloco versão 1.0.0 não está sendo executado, ele ainda está sendo analisado e gerando um erro de coluna inválido.

Todas as sugestões sobre como resolver isso? Idealmente, eu gostaria de proteger o INSERT IGNORE com uma condicional de modo que não está nem analisado, mas que não parece estar acontecendo. Eu poderia dinamicamente executar o INSERT IGNORE s dentro sp_executesql () chama, mas eu prefiro não (exigiria um monte de adaptação).

Obrigado --

--Andy

Publicado 27/08/2009 em 01:51
fonte usuário
Em outras línguas...                            


4 respostas

votos
1

Ok, eu mis-ler a pergunta inicialmente. :-)

Se você alterar as linhas de pastilhas de:

INSERT IGNORE  [foo] ([a], [b], [OrganizationId]) VALUES (N'a', N'b', N'1');

para:

exec('INSERT IGNORE  [foo] ([a], [b], [OrganizationId]) VALUES (''a'', ''b'', ''1'')');

Você não deve ter esse problema, já que o "texto" SQL dentro do exec não será analisado até o exec () é realmente chamado.

Respondeu 27/08/2009 em 02:40
fonte usuário

votos
1

Infelizmente, este é semelhante ao problema que você começa quando (dentro de um proc armazenado) você soltar uma tabela temporária e, em seguida, recriá-lo. O analisador vai reclamar que já existe, não parecendo perceber que a tabela temporária apenas se tiver caído.

Se você separá-lo com declarações ir, então você deve encontrar o sistema irá reavaliar cada seção como se trata para ele.

Roubar

Respondeu 27/08/2009 em 02:08
fonte usuário

votos
0

Você já tentou SQL dinâmico? Infelizmente, o analisador irá verificar todo o script antes de executá-lo, de modo que qualquer coluna inválido irá parar a execução.

Respondeu 27/08/2009 em 02:22
fonte usuário

votos
0

Nós usamos uma configuração quase idêntica para lidar com controle de versão do esquema.

Em geral, sua abordagem é totalmente som. Fomos correndo com esta configuração geral para vários anos. Basicamente, para lidar com quaisquer alterações no esquema destrutivas ou não compatíveis, que execute os remendos como uma parte de uma construção CruiseControl.NET automatizado.

Portanto, a nossa construção do banco de dados se parece com isso ...

  • Restaurar o backup versão de produção atual.
  • Verifique versão do DB restaurada
  • Executar todos os patches (estes são nomeados por convenção usando major.minor.sql) que são posterior à versão indicado na tabela [Versões].

Desta forma, podemos reconstruir todo o dia sem problemas, não importa o que o patch faz. Isso também garante que quando nós implantar à produção que não há problemas, uma vez que já implantados pela 1000x PRODUÇÃO db durante o desenvolvimento.

Respondeu 27/08/2009 em 02:12
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more