Я пытаюсь создать триггер, чтобы изменить параметры сортировки базы данных при ее создании, но как я могу поймать имя базы данных для использования внутри триггера?
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
Очевидно, это не работает.
sql-server
sql-server-2008-r2
trigger
collation
ddl
Racer SQL
источник
источник
Ответы:
Вы не можете, вообще говоря, выпускать
ALTER DATABASE
в Триггере (или в любой Транзакции, в которой есть другие заявления). Если вы попытаетесь, вы получите следующую ошибку:Причина, по которой эта ошибка не встречалась в ответе @ sp_BlitzErik, является результатом предоставленного конкретного теста: показанная выше ошибка является ошибкой во время выполнения, тогда как ошибка, обнаруженная в его ответе, является ошибкой времени компиляции. Эта ошибка во время компиляции препятствует выполнению команды и, следовательно, не имеет времени выполнения. Мы можем увидеть разницу, запустив следующее:
Вышеуказанная партия будет с ошибкой, а следующая не будет:
Это оставляет вам два варианта:
Зафиксируйте транзакцию в триггере DDL так, чтобы в транзакции не было других операторов. Это не очень хорошая идея, если есть несколько триггеров DDL, которые могут быть запущены
CREATE DATABASE
оператором, и, возможно, это плохая идея в целом, но она работает ;-). Хитрость заключается в том, что вам также нужно начать новую транзакцию в триггере, иначе SQL Server заметит, что начальное и конечное значения@@TRANCOUNT
не совпадают, и выдаст ошибку, связанную с этим. Приведенный ниже код делает именно это, а также выдает только,ALTER
если сортировка не является желаемой, иначе она пропускаетALTER
команду.Тест с:
Используйте SQLCLR, чтобы установить обычный / внешнийSqlConnection
,Enlist = false;
в строке подключения, для выдачиALTER
команды, так как она не будет частью транзакции.Похоже, что SQLCLR на самом деле не вариант, хотя не из-за какого-либо конкретного ограничения SQLCLR. Каким-то образом ввод текста « как это не будет частью транзакции » непосредственно выше недостаточно подчеркивал факт наличия активной транзакции вокруг
CREATE DATABASE
операции. Проблема здесь заключается в том, что хотя SQLCLR можно использовать для выхода за пределы текущей транзакции, другой сеанс по-прежнему не может модифицировать базу данных, создаваемую в данный момент, до тех пор , пока эта первоначальная транзакция не будет зафиксирована.Это означает, что сессия A создает транзакцию для создания базы данных и запуска триггера. Триггер, использующий SQLCLR, создаст сеанс B, чтобы изменить созданную базу данных, но транзакция еще не зафиксирована, поскольку она находится в режиме ожидания до завершения сеанса B, чего не может быть, потому что она ожидает этой первоначальной транзакции для полный. Это тупик, но он не может быть обнаружен как таковой SQL Server, поскольку он не знает, что сеанс B был создан чем-то внутри сеанса A. Это поведение можно увидеть, заменив первую часть
IF
оператора в примере выше в # 1 со следующим:-t 15
Переключатель для SQLCMD задает команду / запрос тайм - аут , так что тест не ждать вечно с тайм - аут по умолчанию. Но вы можете установить его длительностью более 15 секунд и в другом сеансе проверить,sys.dm_exec_requests
чтобы увидеть все происходящие прекрасные блокировки ;-).Поставьте в очередь событие где-нибудь, чтобы потом прочитать из этой очереди и выполнить соответствующий
ALTER DATABASE
оператор. Это позволитCREATE DATABASE
оператору завершиться и выполнить транзакцию, после чегоALTER DATABASE
оператор может быть выполнен. Сервисный брокер может быть использован здесь. ИЛИ, создайте таблицу, вставьте триггер в эту таблицу, затем задание агента SQL Server вызовет хранимую процедуру, которая читает из этой таблицы и выполняетALTER DATABASE
инструкцию, а затем удаляет запись из таблицы очереди.ОДНАКО вышеупомянутые опции в основном предоставлены, чтобы помочь в сценариях, где кто-то действительно должен сделать некоторый тип в
ALTER DATABASE
пределах DDL Trigger. В этом конкретном сценарии, если вы действительно не хотите, чтобы какие-либо базы данных использовали параметры сортировки по умолчанию на уровне системы / экземпляра, вам, вероятно, лучше всего подойдут:Setup.exe /Q /ACTION=Rebuilddatabase /INSTANCENAME=<instancename> /SQLCOLLATION=...
, эта опция воссоздает системные базы данных, поэтому вам потребуется для создания сценариев объектов уровня сервера и т. д. для последующего создания, а также повторного применения исправлений и т. д., FUN, FUN, FUN).Или, для приключений в глубине души, есть недокументированный (то есть неподдерживаемый
sqlservr.exe -q
вариант « используйте на свой страх и риск, но возможно, очень хорошо работает»), который обновляет ВСЕ БД и ВСЕ столбцы (см. Изменение «Сравнение экземпляра, баз данных и всех столбцов во всех пользовательских базах данных: что может быть неправильным» для подробного описания поведения этой опции, а также потенциальной области влияния).Независимо от выбранного варианта: всегда проверяйте наличие резервных копий
master
иmsdb
перед попыткой таких действий .Причиной того, что стоит изменить сортировку по умолчанию на уровне сервера, является то, что по умолчанию сортировка экземпляра (т.е. уровня сервера) контролирует несколько функциональных областей, которые могут привести к неожиданному / противоречивому поведению, поскольку все ожидают, что строковые операции будут функционировать в соответствии с параметрами сортировки по умолчанию для всех ваших пользовательских баз данных:
Сортировка по умолчанию для строковых столбцов во временных таблицах. Эта проблема возникает только при сравнении / Объединение с другими строковыми столбцами, ЕСЛИ существует несоответствие между двумя строковыми столбцами. Проблема здесь заключается в том, что, если не указать Collation в явном виде через
COLLATE
ключевое слово, гораздо более вероятно (хотя и не гарантировано) столкнуться с проблемами.Это не проблема для типа данных XML, табличных переменных или автономных баз данных.
Метаданные уровня экземпляра. Например, в
name
полеsys.databases
будет использоваться сопоставление по умолчанию на уровне экземпляра. Другие представления системного каталога также затронуты, но у меня нет полного списка.Метаданные на уровне базы данных, такие как
sys.objects
иsys.indexes
, не затрагиваются.@variable
)GOTO
этикеткиНапример, если сопоставление на уровне экземпляра нечувствительно к регистру, а сопоставление на уровне базы данных является двоичным (т. Е. Оканчивается на
_BIN
или_BIN2
), тогда разрешение имен объектов уровня базы данных будет двоичным (например[TableA] <> [tableA]
), а имена переменных будут учитывать нечувствительность к регистру (например@VariableA = @variableA
).источник
Вам нужно будет использовать динамический SQL и функцию EVENTDATA () .
Просто саб в вашем сопоставлении для моего поддельного .
Теперь, когда я создаю базу данных ...
Я получаю это сообщение (из печати):
Просто отметьте, что если другие базы данных (включая tempdb) используют другие параметры сортировки, вы можете столкнуться с проблемами при сравнении строковых данных. Вы должны были бы добавить предложения COLLATE к сравнениям строк, где регистр или акценты имеют значение, и даже если это не так, вы можете столкнуться с ошибками. Связанный вопрос, где я столкнулся с подобной проблемой кода здесь .
источник
Вы не можете
ALTER DATABASE
в триггере. Вам нужно проявить творческий подход с оценкой и исправлением. Что-то вроде:Хотя вы не должны использовать sp_MSforeachdb .
источник