Есть ли способ отключить триггер SQL Server только для определенной области выполнения?

#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:

Я только что столкнулся с той же проблемой и пришел к следующему решению, которое работает для меня.

  1. Создайте постоянную таблицу БД, содержащую по одной записи для каждого триггера, который вы хотите отключить (например, refTriggerManager); каждая строка содержит имя триггера (например, strTriggerName = ‘myTrigger’) и битовый флаг (например, blnDisabled, по умолчанию 0).
  2. В начале тела триггера найдите strTriggerName = ‘myTrigger’ в refTriggerManager. Если blnDisabled = 1, то возвращайтесь без выполнения остальной части кода триггера, в противном случае продолжайте код триггера до завершения.
  3. В сохраненном процессе, в котором вы хотите отключить триггер, выполните следующие действия:

НАЧАТЬ ТРАНЗАКЦИЮ

ОБНОВЛЕНИЕ 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')