Estou tentando criar um gatilho, para alterar o agrupamento de um banco de dados em sua criação, mas como posso pegar o nome do banco de dados para usar dentro do gatilho?
USE master
GO
CREATE TRIGGER trg_DDL_ChangeCOllationDatabase
ON ALL SERVER
FOR CREATE_DATABASE
AS
declare @databasename varchar(200)
set @databasename =db_name()
ALTER DATABASE @databasename COLLATE xxxxxxxxxxxxxxxxxxx
GO
Obviamente, isso não está funcionando.
sql-server
sql-server-2008-r2
trigger
collation
ddl
Racer SQL
fonte
fonte
Respostas:
De maneira geral, você não pode emitir
ALTER DATABASE
um acionador (ou qualquer transação que contenha outras instruções). Se você tentar, você receberá o seguinte erro:A razão pela qual esse erro não foi encontrado na resposta do @ sp_BlitzErik é resultado do caso de teste específico fornecido: o erro mostrado acima é um erro em tempo de execução, enquanto o erro encontrado em sua resposta é um erro em tempo de compilação. Esse erro em tempo de compilação impede a execução do comando e, portanto, não há "tempo de execução". Podemos ver a diferença executando o seguinte:
O lote acima apresentará um erro, enquanto o seguinte não:
Isso deixa você com duas opções:
Confirme a transação no gatilho DDL, para que não haja outras instruções na transação. Essa não é uma boa ideia se houver vários gatilhos DDL que podem ser disparados por uma
CREATE DATABASE
instrução e possivelmente seja uma má ideia em geral, mas funciona ;-). O truque é que você também precisa iniciar uma nova transação no gatilho. O SQL Server observará que os valores inicial e final de@@TRANCOUNT
não coincidem e gerará um erro relacionado a isso. O código abaixo faz exatamente isso e também emite apenasALTER
se o agrupamento não for o desejado, caso contrário, ele ignora oALTER
comando.Teste com:
Use SQLCLR para estabelecer um regular / externoSqlConnection
,Enlist = false;
na String de Conexão, para emitir oALTER
comando, pois isso não fará parte da Transação.Parece que o SQLCLR não é realmente uma opção, embora não seja devido a qualquer limitação específica do SQLCLR. De alguma forma, digitar " como isso não fará parte da transação " diretamente acima não destacou suficientemente o fato de que há, de fato, uma transação ativa em torno da
CREATE DATABASE
operação. O problema aqui é que, embora o SQLCLR possa ser usado para sair da Transação atual, ainda não há como outra Sessão modificar o Banco de Dados que está sendo criado atualmente até que a Transação inicial seja confirmada.Significado, a Sessão A cria a transação para a criação do banco de dados e o acionamento do gatilho. O Disparador, usando SQLCLR, criará a Sessão B para modificar o Banco de Dados que foi criado, mas a Transação ainda não foi confirmada, pois permanece em espera até a Sessão B ser concluída, o que não é possível porque está aguardando a transação inicial completo. Isso é um impasse, mas não pode ser detectado como tal pelo SQL Server, pois não sabe que a Sessão B foi criada por algo dentro da Sessão A. Esse comportamento pode ser visto substituindo a primeira parte da
IF
instrução no exemplo acima em # 1 com o seguinte:A
-t 15
opção para SQLCMD define o tempo limite do comando / consulta para que o teste não espere um tempo para sempre com o tempo limite padrão. Mas você pode configurá-lo para mais de 15 segundos e, em outra sessão, verifiquesys.dm_exec_requests
se todos os adoráveis bloqueios estão acontecendo ;-).Enfileire o evento em algum lugar que lerá a partir dessa fila e executará a
ALTER DATABASE
instrução apropriada . Isso permitirá que aCREATE DATABASE
instrução seja concluída e sua transação seja confirmada, após a qual umaALTER DATABASE
instrução pode ser executada. O Service Broker pode ser usado aqui. OU, crie uma tabela, faça com que o Trigger seja inserido nessa tabela e faça com que um trabalho do SQL Server Agent chame um Procedimento Armazenado que leia essa tabela e execute aALTER DATABASE
instrução e remova o registro da fila Tabela.NO ENTANTO, as opções acima são fornecidas principalmente para ajudar em cenários em que alguém realmente precisa fazer algum tipo
ALTER DATABASE
dentro de um gatilho DDL. Nesse cenário específico, se você realmente não deseja que nenhum banco de dados esteja usando o agrupamento padrão no nível do sistema / instância, provavelmente será melhor atendido por:Setup.exe /Q /ACTION=Rebuilddatabase /INSTANCENAME=<instancename> /SQLCOLLATION=...
, essa opção recria os bancos de dados do sistema, portanto, você precisará para criar scripts de objetos no nível do servidor, etc, para recriar mais tarde, além de reaplicar patches etc., FUN, FUN, FUN).Ou, para os aventureiros de coração, existe a opção não documentada (isto é, sem suporte, use por seu próprio risco, mas pode funcionar muito bem)
sqlservr.exe -q
que atualiza TODOS os DBs e TODAS as colunas (consulte Alteração o agrupamento da instância, os bancos de dados e todas as colunas em todos os bancos de dados do usuário: o que pode dar errado? para obter uma descrição detalhada do comportamento dessa opção e do escopo potencial do impacto).Independentemente da opção escolhida: sempre faça backups
master
emsdb
antes de tentar essas coisas.A razão pela qual valeria a pena o esforço para alterar o agrupamento padrão no nível do servidor é que o agrupamento padrão da instância (ou seja, no nível do servidor) controla algumas áreas funcionais que podem levar a um comportamento inesperado / inconsistente, pois todos esperam que as operações de sequência funcionem ao longo das linhas do agrupamento padrão para todos os bancos de dados do usuário:
Agrupamento padrão para colunas de sequência em tabelas temporárias. Esse é um problema apenas ao comparar / Unioning com outras colunas de string SE houver uma incompatibilidade entre as duas colunas de string. O problema aqui é que, ao não especificar o agrupamento explicitamente por meio da
COLLATE
palavra-chave, é muito mais provável (embora não garantido) que ocorra problemas.Isso não é um problema para o tipo de dados XML, variáveis de tabela ou bancos de dados contidos.
Meta-dados no nível da instância. Por exemplo, o
name
campo emsys.databases
usará o agrupamento padrão no nível da instância. Outras visualizações do catálogo do sistema também são afetadas, mas não tenho a lista completa.Os metadados no nível do banco de dados, como
sys.objects
esys.indexes
, não são afetados.@variable
)GOTO
etiquetasPor exemplo, se o agrupamento no nível da instância não diferencia maiúsculas de minúsculas, enquanto o agrupamento no nível do banco de dados é binário (ou seja, termina em
_BIN
ou_BIN2
), a resolução de nomes de objetos no nível do banco de dados será binária (por exemplo[TableA] <> [tableA]
), mas os nomes de variáveis permitem a diferenciação de maiúsculas e minúsculas (por exemplo@VariableA = @variableA
).fonte
Você precisaria usar SQL dinâmico e a função EVENTDATA () .
Apenas sub no seu agrupamento para o meu falso .
Agora, quando eu crio um banco de dados ...
Recebo esta mensagem (da impressão):
Observe que, se outros bancos de dados (incluindo tempdb) usarem agrupamentos diferentes, você poderá encontrar problemas ao comparar os dados da string. Você precisaria adicionar cláusulas COLLATE para comparações de cadeias de caracteres em que maiúsculas ou minúsculas são importantes e, mesmo que não sejam, você pode encontrar erros. Pergunta relacionada onde encontrei um problema de código semelhante aqui .
fonte
Você não pode
ALTER DATABASE
em um gatilho. Você precisará ser criativo com a avaliação e a correção. Algo como:Embora você não deva usar sp_MSforeachdb .
fonte