Devido à lógica de negócios, precisamos de uma nova coluna em uma tabela que seja essencial para garantir que ela seja sempre preenchida. Portanto, deve ser adicionado à tabela como NOT NULL
. Ao contrário das perguntas anteriores que explicam como fazer isso manualmente , isso precisa ser gerenciado pela publicação do SSDT.
Eu tenho batido minha cabeça contra a parede por um tempo por causa dessa tarefa que parece simples devido a algumas realizações:
- Um valor padrão não é apropriado e não pode ser uma coluna computada. Talvez seja uma coluna de chave estrangeira, mas para outras não podemos usar um valor falso como 0 ou -1 porque esses valores podem ter significado (por exemplo, dados numéricos).
- Adicionar a coluna em um script de pré-implantação falhará na publicação quando tentar criar automaticamente a mesma coluna, uma segunda vez (mesmo que o script de pré-implantação seja escrito para ser idempotente) (este é realmente agravante, pois posso de outra forma pense em uma solução fácil)
- A alteração da coluna para NOT NULL em um script pós-implantação será revertida toda vez que ocorrer a atualização do esquema SSDT (portanto, no mínimo, nossa base de código não corresponderá entre o controle de origem e o que está realmente no servidor)
- Adicionar a coluna como anulável agora com a intenção de mudar para NOT NULL no futuro não funciona em várias ramificações/bifurcações no controle de origem, pois os sistemas de destino não necessariamente terão a tabela no mesmo estado na próxima vez que forem atualizados ( não que esta seja uma boa abordagem de qualquer maneira IMO)
A abordagem que ouvi de outras pessoas é atualizar diretamente a definição da tabela (para que a atualização do esquema seja consistente), escrever um script de pré-implantação que mova todo o conteúdo da tabela para uma tabela temporária com a nova lógica de preenchimento de coluna incluída e, em seguida, mova as linhas de volta em um script pós-implantação. Isso parece arriscado como o inferno, e ainda irrita o Publish Preview quando detecta que uma coluna NOT NULL está sendo adicionada a uma tabela com dados existentes (já que a validação é executada antes do script de pré-implantação).
Como devo adicionar uma nova coluna não anulável sem arriscar dados órfãos ou mover dados para frente e para trás em cada publicação com longos scripts de migração que são inerentemente arriscados?
Obrigado.
Vou compartilhar como eu fiz isso no passado. Ele foi projetado para resolver a limitação específica de scripts de pré-implantação que você chama em seu segundo ponto:
Por que os scripts de pré-implantação não funcionam para isso
Quando você implanta um projeto SSDT, a maneira como ele une as coisas é assim (um pouco simplificada, mas em geral):
Quando existe uma nova coluna no dacpac e não no banco de dados de destino, a etapa 2 gerará o código para adicionar essa coluna. Portanto, se o script de pré-implantação adicionar essa coluna, a parte principal do script falhará (porque assume que a coluna não existe, com base nos resultados da comparação de esquema na etapa 1)
Solução: script pré-SSDT
Martin Smith mencionou essa opção em um comentário e é a solução que funcionou melhor para mim até agora:
Os passos para implementar esta solução em geral são:
No final, isso permite que você adicione a coluna usando qualquer código personalizado que desejar, preenchido usando lógica de negócios complexa, no script pré-SSDT.
Você também adiciona a definição de coluna no projeto SSDT (para que o controle de origem ainda corresponda ao estado real do banco de dados). Mas quando a comparação de esquema é executada, ela não vê alterações relacionadas a essa coluna (porque você já a implantou).
Outros usos do pré-SSDT
Muitas vezes, ao testar implantações, vejo que o SSDT executa uma operação de "reconstrução de tabela"* quando é completamente desnecessária. É aqui que uma nova tabela é criada com o esquema atualizado, todos os dados são copiados para essa tabela, a tabela antiga é eliminada e a nova tabela é renomeada para substituir a tabela antiga.
Isso pode levar ao crescimento massivo do arquivo de log de transações e outros problemas se a tabela for grande. Se eu perceber que uma alteração de esquema está causando isso, eu mesmo farei a alteração no pré-SSDT (que geralmente é uma
ALTER TABLE
instrução simples) e evitarei a reconstrução da tabela.isso é uma boa ideia?
Eu penso que sim. Se você ler Criticando duas abordagens diferentes para entregar bancos de dados: Migrations vs state por Alex Yates, isso é essencialmente combinar um pouco as duas abordagens. O SSDT é baseado em estado, mas incorporamos uma etapa de migração (antes do SSDT) para lidar com alguns dos cenários mais complexos com os quais o SSDT simplesmente não tem como lidar de maneira geral.
Ao fazer algumas pesquisas enquanto escrevia esta resposta, essa é realmente uma abordagem muito comum discutida na comunidade de usuários do SSDT quando você sabe o que procurar. Eu vi isso chamado:
Etc. Aqui está um ótimo artigo que cobre muitos dos pontos que mencionei acima:
Scripts de pré-comparação e pré-implantação para SSDT
E um do Red Gate (na seção #4 – Mudando do tipo de sistema para o tipo definido pelo usuário ) que também se refere a isso como pré-comparação:
Como corrigir dez problemas de implantação SSDT, com ou sem ReadyRoll
Então, qual é o objetivo dos scripts de pré-implantação?
Martin aponta que ele não encontrou " muito uso para scripts de pré-implantação ". Eu tendo a me sentir da mesma maneira. Mas há cenários em que eles podem ser úteis.
Um exemplo que um colega de trabalho apontou para mim foi armazenar alguns dados em uma tabela temporária para serem usados no script de pós-implantação (digamos que você esteja movendo uma coluna de uma tabela para outra).
*A reconstrução da tabela se parece com isso, o que é horrível, certo?