#sql-server #tsql #triggers
Вопрос:
В SQL Server 2005 есть ли способ для триггера узнать, какой объект отвечает за запуск триггера? Я хотел бы использовать это, чтобы отключить триггер для одной хранимой процедуры.
Есть ли какой-либо другой способ отключить триггер только для текущей транзакции? Я мог бы использовать следующий код, но, если я не ошибаюсь, это также повлияло бы на параллельные транзакции, что было бы плохо.
DISABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL } ON { object_name | DATABASE | ALL SERVER } [ ; ]
ENABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL } ON { object_name | DATABASE | ALL SERVER } [ ; ]
Если возможно, я хотел бы избежать использования поля «NoTrigger» в моей таблице и выполнения a NoTrigger = null
, потому что я хотел бы, чтобы таблица была как можно меньше.
Причина, по которой я хотел бы избежать триггера, заключается в том, что он содержит логику, которая важна для обновления таблицы вручную, но моя хранимая процедура позаботится об этой логике. Поскольку это будет широко используемая процедура, я хочу, чтобы она была быстрой.
Триггеры накладывают дополнительные накладные расходы на сервер, поскольку они инициируют неявную транзакцию. Как только триггер выполняется, запускается новая неявная транзакция, и любой поиск данных в транзакции будет блокировать затронутые таблицы.
От: http://searchsqlserver.techtarget.com/tip/1,289483,sid87_gci1170220,00.html#триггер
Комментарии:
1. вы можете использовать функцию Exec для вызова и включения триггеров из хранимой процедуры. Пример: ` EXEC (‘ВКЛЮЧИТЬ ТРИГГЕР dbo. Имя триггера в dbo. Триггерная таблица’)`
Ответ №1:
Я только что видел эту статью, недавно опубликованную в Центральном информационном бюллетене SQL Server, и, похоже, она предлагает способ, который вы можете счесть полезным, используя Context_Info для подключения:
http://www.mssqltips.com/tip.asp?tip=1591
ПРАВКА Террапина:
Приведенная выше ссылка включает в себя следующий код:
USE AdventureWorks;
GO
-- creating the table in AdventureWorks database
IF OBJECT_ID('dbo.Table1') IS NOT NULL
DROP TABLE dbo.Table1
GO
CREATE TABLE dbo.Table1(ID INT)
GO
-- Creating a trigger
CREATE TRIGGER TR_Test ON dbo.Table1 FOR INSERT,UPDATE,DELETE
AS
DECLARE @Cinfo VARBINARY(128)
SELECT @Cinfo = Context_Info()
IF @Cinfo = 0x55555
RETURN
PRINT 'Trigger Executed'
-- Actual code goes here
-- For simplicity, I did not include any code
GO
Если вы хотите предотвратить выполнение триггера, вы можете сделать следующее:
SET Context_Info 0x55555
INSERT dbo.Table1 VALUES(100)
Комментарии:
1. Я действительно хотел бы, чтобы был лучший способ для переменных/состояния сеанса (?): что, если
Context_Info
уже используется для других целей? (например, реализация многозадачности.) 🙁2. @TheMouthofaCow Я тебя не понимаю. Ваш комментарий верен, но я не понимаю, почему он важен для варианта использования операции. По общему признанию, мне вообще не нравится идея отключения триггера (даже с проверкой контекста), но я не уверен, что то, что вы говорите, здесь неправильно.
3. @TheMouthofaCow К счастью, нам разрешено задавать вопросы, которые имеют больший контекст, чем позволяет заголовок. Вы отвечаете на все вопросы, читая название и игнорируя фактическую суть вопроса?
4. @TheMouthofaCow Ответ решил проблему, с которой столкнулась операция. То, что ответ не решает ваш вопрос, не имеет значения. Тем не менее, ваши голоса (вверх или вниз) вы можете отдать по своему усмотрению, будь то в духе сети SE или иным образом.
5. @TheMouthofaCow Должны ли мы изменить заголовок на «Есть ли способ отключить триггер SQL Server только для определенной области выполнения?» Это решило бы все.
Ответ №2:
Если ваш триггер вызывает проблемы с производительностью в приложении, то лучший подход-удалить все обновления таблицы вручную и потребовать, чтобы все обновления проходили через хранимые процедуры вставки/обновления, которые содержат правильную логику обновления. Затем вы можете полностью снять спусковой крючок.
Я предлагаю отказать в разрешениях на обновление таблиц, если больше ничего не работает.
Это также решает проблему дублирования кода. Дублирование кода в обновлении SP и в триггере является нарушением хороших принципов разработки программного обеспечения и будет проблемой обслуживания.
Комментарии:
1. Категорически не согласен. Существует множество способов изменения данных, и бизнес-правила должны применяться на уровне базы данных. Если триггер работает плохо, его следует переписать, но никогда не удалять и не отключать. Своим подходом вы можете создать очень плохие проблемы с целостностью данных.
2. Хранимая процедура уже поддерживает целостность данных, и доступ к таблице может быть ограничен, так что любой процесс, желающий обновить ее, должен использовать proc. Если вы разрешаете доступ к таблице многими способами, триггер-это просто симптом проблемы, а не ее решение.
3. если можно сделать так, как предлагает Джеффри, это было бы лучшим решением.
4. В то время как я на 100% согласен с вами во всем, что я создаю, другие (как и я) застряли в борьбе с неаккуратными сторонними приложениями и их внутренними базами данных, которые загружены триггерами. Каждую ночь у нас есть задания, которые обновляют данные, и это происходит слишком медленно, когда срабатывают все их триггеры, или некоторые триггеры заставляют обновленные записи возвращаться через интерфейс — когда это не нужно для вносимых нами изменений.
Ответ №3:
ИЗМЕНИТЬ ТАБЛИЦУ tbl ОТКЛЮЧИТЬ ТРИГГЕР trg
http://doc.ddart.net/mssql/sql70/aa-az_5.htm
Хотя я не понимаю смысла вашего 1-го абзаца
Комментарии:
1. Мой первый абзац — В моем триггере есть функция, которую я могу использовать, чтобы получить имя сохраненного процесса, который запустил мой триггер? Что-то вроде использования
if (trigger_nestlevel(object_id('NameOfTrigger')) = 0)
, чтобы убедиться, что триггер не вызывает сам себя
Ответ №4:
Поскольку вы указываете, что триггер содержит логику для обработки всех обновлений, даже обновлений вручную, то логика должна быть именно там. Приведенный вами пример, в котором хранимая процедура «позаботится об этой логике», подразумевает дублирование кода. Кроме того, если вы хотите быть уверены, что к каждому оператору ОБНОВЛЕНИЯ применяется эта логика независимо от автора, то триггер-это подходящее место для этого. Что происходит, когда кто-то создает процедуру, но забывает повторить логику еще раз? Что происходит, когда приходит время изменить логику?
Ответ №5:
Не уверен, что это хорошая идея, но, похоже, она мне подходит. Транзакция должна предотвращать вставки в таблицу из других процессов, пока триггер отключен.
IF OBJECT_ID('dbo.TriggerTest') IS NOT NULL
DROP PROCEDURE dbo.TriggerTest
GO
CREATE PROCEDURE [dbo].[TriggerTest]
AS
BEGIN TRANSACTION trnInsertTable1s
;
DISABLE TRIGGER trg_tblTable1_IU ON tblTable1
;
BEGIN -- Procedure Code
PRINT '@@trancount'
PRINT @@TRANCOUNT
-- Do Stuff
END -- Procedure Code
;
ENABLE TRIGGER trg_tblTable1_IU ON tblTable1
IF @@ERROR <> 0 ROLLBACK TRANSACTION
ELSE COMMIT TRANSACTION
Комментарии:
1. Это странный способ создания или обновления существующего sproc (есть неутвержденное редактирование, чтобы изменить его на простое удаление/создание, и исправить ошибку блочной копии в ПРОЦЕДУРЕ УДАЛЕНИЯ). Есть ли какая-то польза в том, чтобы делать это таким образом? Спасибо!
2. Это просто используется в качестве дополнения/обновления. Если вы откажетесь от процедуры, вы можете быть уверены, что «СОЗДАТЬ ПРОЦЕДУРУ» будет работать. Я использовал это при хранении хранимых процедур в системе управления версиями. Каждый файл .sql представляет собой сценарий, который удаляется, если он существует, а затем текущая хранимая процедура. Это не имеет отношения к ответу. Также можно использовать If/Else, но содержимое хранимой процедуры необходимо будет дублировать.
3. Конечно, я просто заметил это из-за правки. Да, у нас обычно есть «если процедура существует, удалите, создайте» — я просто раньше не видел «удалить, создать как» выбрать 1″, обновить».
4. Спасибо, что спросили. Какое-то время я об этом не думал. Это стандартный код. В нем добавлена функция создать как выбрать 1, а также добавлены права на выполнение для стандартной учетной записи. Затем следующий код выполняет изменение и имеет фактический код. Отредактируйте шаблон с фактической учетной записью, и начало каждой хранимой процедуры будет одинаковым, за исключением имени, и все они имеют права на выполнение для стандартной учетной записи. Полезно для некоторых ситуаций. Надеюсь, это поможет.
Ответ №6:
Не отключайте триггер. Вы правы, это будет отключено для любых одновременных транзакций.
Почему вы хотите отключить триггер? Что он делает? Почему триггер создает проблему? Обычно отключать тигру с точки зрения целостности данных-плохая идея.
Ответ №7:
Подумайте о том, чтобы переписать триггер для повышения производительности, если проблема заключается в производительности.
Ответ №8:
Я немного пошутил на этот счет. С одной стороны, я очень против триггера в основном потому, что это еще одно место, где я могу искать код, выполняющийся в моей таблице, в дополнение к причинам, изложенным в статье, связанной с вопросом.
С другой стороны, если у вас есть логика для обеспечения стабильных и неизменяемых бизнес-правил или действий между таблицами (например, ведение таблицы истории), то было бы безопаснее включить это в триггер, чтобы авторам процедур и программистам не нужно было с этим иметь дело — это просто работает.
Итак, моя рекомендация состоит в том, чтобы поместить необходимую логику в ваш триггер, а не в этот один процесс, который неизбежно вырастет до нескольких процессов с одним и тем же исключением.
Ответ №9:
Я только что столкнулся с той же проблемой и пришел к следующему решению, которое работает для меня.
- Создайте постоянную таблицу БД, содержащую по одной записи для каждого триггера, который вы хотите отключить (например, refTriggerManager); каждая строка содержит имя триггера (например, strTriggerName = ‘myTrigger’) и битовый флаг (например, blnDisabled, по умолчанию 0).
- В начале тела триггера найдите strTriggerName = ‘myTrigger’ в refTriggerManager. Если blnDisabled = 1, то возвращайтесь без выполнения остальной части кода триггера, в противном случае продолжайте код триггера до завершения.
- В сохраненном процессе, в котором вы хотите отключить триггер, выполните следующие действия:
НАЧАТЬ ТРАНЗАКЦИЮ
ОБНОВЛЕНИЕ refTriggerManager УСТАНОВИТЕ ЗНАЧЕНИЕ blnDisabled = 1, ГДЕ strTriggerName = ‘myTrigger’
/* ОБНОВИТЕ таблицу, которой принадлежит «myTrigger», но которую вы хотите отключить. Поскольку refTriggerManager.blnDisabled = 1, ‘myTrigger’ возвращается без выполнения своего кода. */
ОБНОВИТЬ refriggermanager УСТАНОВИТЬ blnDisabled= 0, ГДЕ triggerName = «Mytriger»
/* Дополнительный код окончательного ОБНОВЛЕНИЯ, который запускает триггер. Поскольку refriggermanager.blnDisabled = 0, «MYTRIGER» выполняется полностью. */
ЗАФИКСИРОВАТЬ ТРАНЗАКЦИЮ
Все это происходит в рамках транзакции, поэтому она изолирована от внешнего мира и не влияет на другие обновления в целевой таблице.
Кто-нибудь видит какие — либо проблемы с этим подходом?
Билл
Комментарии:
1. Это выглядит нарушенным
READ COMMITTED SNAPSHOT
, и в стандартеREAD COMMITTED
оно блокирует все остальные транзакции DML до завершения, предотвращая эффективное использование блокировок на уровне строк. В основном вы реализовали искаженную версию принятого ответа. Если бы это не был такой старый вопрос, я бы, вероятно, дал вам -1 только за ужасную венгерскую нотацию и отсутствие форматирования.
Ответ №10:
Я согласен с некоторыми другими ответами. Не отключайте триггер.
Это чистое мнение, но я избегаю таких триггеров, как чума. Я обнаружил очень мало случаев, когда триггер использовался для обеспечения соблюдения правил базы данных. В моем опыте есть очевидные крайние случаи, и у меня есть только мой опыт, на котором я могу сделать это заявление. Я обычно видел триггеры, используемые для вставки некоторых реляционных данных (что должно быть сделано из бизнес-логики), для вставки данных в таблицу отчетов, т. Е. для денормализации данных (что может быть сделано с помощью процесса вне транзакции) или для преобразования данных каким-либо образом.
Существуют законные способы использования триггеров, но я думаю, что в повседневном деловом программировании их немного и они находятся далеко друг от друга. Это может не помочь в вашей текущей проблеме, но вы можете рассмотреть возможность полного удаления триггера и выполнения работы, которую выполняет триггер, каким-либо другим способом.
Комментарии:
1. «Операторы DDL не выполняются в контексте транзакции?» Вы можете попробовать и сами убедиться, что это неправильно. Кстати, сравнение SQL Red Gate может генерировать сценарий развертывания, который выполняется как одна транзакция.
2. Ты прав. Я думаю, что мое пребывание в стране Оракулов, должно быть, отравило меня.
Ответ №11:
вы можете использовать функцию «Exec» для вызова и включения триггеров из хранимой процедуры. Пример: EXEC ('ENABLE TRIGGER dbo.TriggerName on dbo.TriggeredTable')