Estoy tratando de crear un disparador, para alterar la recopilación de una base de datos en su creación, pero ¿cómo puedo atrapar el nombre de la base de datos para usar dentro del disparador?
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, esto no está funcionando.
sql-server
sql-server-2008-r2
trigger
collation
ddl
Racer SQL
fuente
fuente
Respuestas:
En general, no puede emitir
ALTER DATABASE
dentro de un Disparador (o cualquier Transacción que contenga otras declaraciones). Si lo intentas, obtendrás el siguiente error:La razón por la que este error no se encontró en la respuesta de @ sp_BlitzErik es el resultado del caso de prueba específico proporcionado: el error que se muestra arriba es un error en tiempo de ejecución, mientras que el error encontrado en su respuesta es un error en tiempo de compilación. Ese error en tiempo de compilación impide la ejecución del comando y, por lo tanto, no hay "tiempo de ejecución". Podemos ver la diferencia ejecutando lo siguiente:
El lote anterior producirá un error, mientras que lo siguiente no:
Esto te deja con dos opciones:
Confirme la transacción dentro del DDL Trigger de modo que no haya otras declaraciones en la transacción. Esta no es una buena idea si hay varios disparadores DDL que pueden ser disparados por una
CREATE DATABASE
declaración, y es posiblemente una mala idea en general, pero funciona ;-). El truco es que también debe comenzar una nueva transacción en el activador, de lo contrario, SQL Server notará que los valores iniciales y finales@@TRANCOUNT
no coinciden y arrojará un error relacionado con eso. El código a continuación hace exactamente esto, y también solo emiteALTER
si la Clasificación no es la deseada, de lo contrario, omite elALTER
comando.Prueba con:
Use SQLCLR para establecer un regular / externoSqlConnection
, conEnlist = false;
la Cadena de conexión, para emitir elALTER
comando ya que eso no será parte de la Transacción.Parece que SQLCLR no es realmente una opción, aunque no debido a ninguna limitación específica de SQLCLR. De alguna manera, escribir " ya que eso no será parte de la Transacción " directamente arriba no resaltó suficientemente el hecho de que hay, de hecho, una Transacción activa alrededor de la
CREATE DATABASE
operación. El problema aquí es que, si bien SQLCLR se puede usar para salir de la transacción actual, todavía no hay forma de que otra sesión modifique la base de datos que se está creando actualmente hasta que se confirme la transacción inicial.Es decir, la sesión A crea la transacción para la creación de la base de datos y el disparo del disparador. El Trigger, utilizando SQLCLR, creará la Sesión B para modificar la Base de datos que se ha creado, pero la Transacción aún no se ha confirmado ya que está en espera hasta que se completa la Sesión B, lo cual no puede porque está esperando que esa Transacción inicial completar. Este es un punto muerto, pero SQL Server no puede detectarlo como tal, ya que no sabe que la sesión B fue creada por algo dentro de la sesión A. Este comportamiento se puede ver al reemplazar la primera parte de la
IF
declaración en el ejemplo arriba en el # 1 con lo siguiente:El
-t 15
conmutador para SQLCMD establece el tiempo de espera del comando / consulta para que la prueba no espere para siempre con el tiempo de espera predeterminado. Pero, puede configurarlo para que dure más de 15 segundos y, en otra sesión, verifiquesys.dm_exec_requests
que se esté ejecutando todo el encantador bloqueo ;-).Ponga en cola el evento en algún lugar que luego leerá de esa cola y ejecutará la
ALTER DATABASE
instrucción apropiada . Esto permitirá que laCREATE DATABASE
declaración se complete y su transacción se confirme, después de lo cualALTER DATABASE
se puede ejecutar una declaración. Service Broker podría usarse aquí. O, cree una tabla, haga que el Trigger se inserte en esa tabla, luego haga que un trabajo del Agente SQL Server llame a un Procedimiento almacenado que lea de esa tabla y ejecute laALTER DATABASE
instrucción y luego elimine el registro de la Tabla de cola.SIN EMBARGO, las opciones anteriores se proporcionan principalmente para ayudar en escenarios en los que alguien realmente necesita hacer algún tipo de
ALTER DATABASE
disparador DDL. En este escenario particular, si realmente no desea que ninguna base de datos esté utilizando la Clasificación predeterminada del nivel de instancia / sistema, entonces probablemente será mejor para usted:Setup.exe /Q /ACTION=Rebuilddatabase /INSTANCENAME=<instancename> /SQLCOLLATION=...
; esta opción recrea las bases de datos del sistema, por lo que necesitará para crear scripts de objetos a nivel de servidor, etc. para recrearlos más tarde, además de volver a aplicar parches, etc., FUN, FUN, FUN).O, para los aventureros de corazón, existe la
sqlservr.exe -q
opción indocumentada (es decir, no admitida, usar bajo su propio riesgo, pero podría funcionar) que actualiza TODOS los DB y TODAS las columnas (consulte Cambiar la recopilación de la instancia, las bases de datos y todas las columnas en todas las bases de datos de usuario: ¿qué podría ir mal? para obtener una descripción detallada del comportamiento de esta opción, así como el alcance potencial del impacto).Independientemente de la opción elegida: siempre asegúrese de tener copias de seguridad de
master
ymsdb
antes de intentar tales cosas.La razón por la que valdría la pena cambiar la Clasificación predeterminada del nivel del servidor es que la Clasificación predeterminada de la instancia (es decir, el nivel del servidor) controla algunas áreas funcionales que podrían conducir a un comportamiento inesperado / inconsistente si todos esperan que las operaciones de cadena funcionen siguiendo las líneas de la Clasificación predeterminada para todas sus Bases de datos de usuario:
Clasificación predeterminada para columnas de cadena en tablas temporales. Este es un problema solo cuando se compara / se une con otras columnas de cadena SI hay una discrepancia entre las dos columnas de cadena. El problema aquí es que cuando no se especifica la Clasificación explícitamente a través de la
COLLATE
palabra clave, es mucho más probable (aunque no está garantizado) tener problemas.Esto no es un problema para el tipo de datos XML, las variables de tabla o las bases de datos contenidas.
Metadatos a nivel de instancia. Por ejemplo, el
name
campo ensys.databases
utilizará la Clasificación predeterminada de nivel de instancia. Otras vistas del catálogo del sistema también se ven afectadas, pero no tengo la lista completa.Los metadatos a nivel de base de datos, como
sys.objects
ysys.indexes
, no se ven afectados.@variable
)GOTO
etiquetasPor ejemplo, si la Intercalación a nivel de instancia no distingue entre mayúsculas y minúsculas, mientras que la Intercalación a nivel de base de datos es binaria (es decir, terminada en
_BIN
o_BIN2
), la resolución de nombre de objeto a nivel de base de datos será binaria (p[TableA] <> [tableA]
. Ej. ), Pero los nombres variables permitirán la insensibilidad a mayúsculas y minúsculas (por ejemplo@VariableA = @variableA
)fuente
Debería usar SQL dinámico y la función EVENTDATA () .
Solo sustituye tu colación por mi falso .
Ahora cuando creo una base de datos ...
Recibo este mensaje (de la impresión):
Solo tenga en cuenta que si otras bases de datos (incluyendo tempdb) usan diferentes intercalaciones, puede tener problemas al comparar datos de cadena. Tendría que agregar cláusulas COLLATE a las comparaciones de cadenas donde la carcasa o los acentos importan, e incluso cuando no lo hacen, puede detectar errores. Pregunta relacionada donde me encontré con un problema de código similar aquí .
fuente
No puedes
ALTER DATABASE
en un gatillo. Tendrá que ser creativo con la evaluación y la corrección. Algo como:Aunque no deberías usar sp_MSforeachdb .
fuente