SQL Server: как отключить триггер для обновления только для текущего сеанса?

15

Я работаю на SQL Server 2008 R2.

У меня есть табличное преимущество, которое имеет триггер AFTER INSERT, UPDATE с именем tiu_benefit .

Я хочу написать инструкцию UPDATE для этой таблицы, чтобы обновить 1 строку, но я не хочу, чтобы ее триггер срабатывал. Я знаю, что могу отключить триггер до UPDATE, а затем включить триггер после UPDATE:

DISABLE TRIGGER tiu_benefit ON benefit;  
GO  
UPDATE benefit SET editor = 'srh' where benefit_id = 9876
GO
ENABLE TRIGGER tiu_benefit ON benefit;  
GO  

Но это отключение и включение триггера повлияет на всех пользователей, вошедших в данный момент. Так что есть вероятность, что другой пользователь запустит UPDATE / INSERT, пока мой скрипт не отключит триггер, что не очень хорошо. Вот почему я хочу только отключить и включить триггер для моей текущей сессии. Является ли это возможным? Если да, пожалуйста, расскажите как.

Благодарность

СРЗ
источник
1
Если вы не можете изменить свой триггер, то ответ - нет.
Цзяо

Ответы:

6

Я провел некоторое тестирование по этому вопросу, и я думаю, что все будет в порядке, если вы запустите свой процесс в одной транзакции.

BEGIN TRANSACTION
GO

DISABLE TRIGGER tiu_benefit ON benefit;
GO

UPDATE benefit
SET editor = 'srh'
WHERE benefit_id = 9876
GO

ENABLE TRIGGER tiu_benefit ON benefit;
GO

--Decide to commit or rollback

--commit
--rollback 

В моем тестировании я только выделил и выполнил BEGIN TRANSACTIONи DISABLE TRIGGERпервый. Затем я открыл новый (второй) окно запроса и попытался запустить различные заявления DML ( SELECT, INSERT, UPDATE DELETE) против базовой таблицы. Все попытки доступа к базовой таблице во втором окне запроса ожидали блокировок, удерживаемых окном с явной транзакцией. Когда я зафиксировал (или откатил) свою явную транзакцию, второе окно смогло получить доступ к таблице.

Скотт Ходжин
источник
Это будет работать, но блокировки могут вызвать непредвиденные проблемы вниз по течению, в зависимости от того, как долго вы держите транзакцию открытой.
СаМ
@CaM - я бы предположил, что обновление в одну строку не займет слишком много времени при условии, что OP фиксирует или откатывает транзакцию быстро. Надеемся, что есть индекс benefit_id:)
Скотт Ходжин
Мне действительно понравилось это решение, так как мне не нужно вносить какие-либо изменения в триггер
srh
18

Чтобы решить вашу проблему, мы должны принять программный подход к проблеме. Есть два маршрута, по которым вы можете пойти здесь. Причина, по которой эти подходы нужны, заключается в том, что вы не можете отключить триггер для определенного оператора, его можно отключить только для всей таблицы.

Вариант 1: Context_Info ()

У Самуэля Ванги на MS SQL Tips был отличный пример:

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)

Context_Info использует следующие системные представления для получения информации о текущем сеансе:

  • sys.dm_exec_requests

  • sys.dm_exec_sessions

  • sys.sysprocesses

Идеология здесь заключается в том, что двоичная строка, которую вы устанавливаете, доступна только для текущего сеанса, поэтому, когда триггер выполняется во время вашего сеанса, он увидит область действия и настройку переменной Context_infoфункции и перейдет к escape-части триггера. вместо.

Вариант 2: временная таблица

У Ицик Бен-Гана есть отличное решение в его книге «Внутри Microsoft SQL Server 2008 Программирование на T-SQL: Программирование на T-SQL», а также в его более поздней книге « Запросы на T-SQL» . Основная проблема с этим по context_infoфункции - незначительные издержки TempDB.

Чтобы испортить сюрприз, но не разрушить сюжет книг (я чувствовал, что их стоит покупать и читать), вы измените свой триггер.

Ваш триггер должен выполнить проверку для временной таблицы. Если временная таблица существует, триггер должен знать, что нужно завершить, а не выполнять действия.

В операторе обновления, который вы хотите выполнить, сначала создайте временную таблицу. Он будет отображаться в той же транзакции, что и триггер, и он будет игнорировать ваш оператор.

Пример триггера:

CREATE TRIGGER TRIGGERNAME ON TABLENAME for INSERT AS

IF OBJECT_ID('tempdb..#FAKETEMPTABLE') IS NOT NULL RETURN;
GO

Пример начального оператора, когда вы не хотите запускать триггер:

CREATE TABLE #FAKETEMPTABLE(col1 SMALLINT);

В целом это для вашего примера:

ALTER TRIGGER tiu_benefit ON benefit FOR 
... 
AS
...
IF OBJECT_ID('tempdb..#FAKETEMPTABLE') IS NOT NULL RETURN;
--... rest of code here
GO

CREATE TABLE #FAKETEMPTABLE(col1 SMALLINT);
UPDATE benefit SET editor = 'srh' where benefit_id = 9876;
GO
Shaulinator
источник
2
Я бы использовал context_info () вместо временной таблицы в триггере. Другими словами, если триггер обнаруживает, что context_info возвращает определенное значение, триггер будет функционировать соответствующим образом. Вы можете обратиться к соответствующему вопросу SO здесь: stackoverflow.com/questions/3025662/…
jyao
1
Вы также можете поставить галочку, аналогичную context_infoиспользованию, original_login()чтобы сказать триггеру никогда не запускаться, если конкретный человек нажимает на триггер.
Кеннет Фишер
2

Я бы использовал либо CONTEXT_INFOновый, либо новый SESSION_CONTEXT. Оба значения основаны на сеансах.

  • CONTEXT_INFOэто одно VARBINARY(128)значение. Это было доступно по крайней мере с SQL Server 2000. Доступно CONTEXT_INFOдля просмотра любому пользователю, так VIEW SERVER STATEкак это поле возвращается sys.dm_exec_sessionsDMV. Я использовал это раньше, и он работает довольно хорошо.

    Установить через SET CONTEXT_INFO
    Получить через CONTEXT_INFO () или sys.dm_exec_sessions

    В зависимости от типа значения, которое вы храните CONTEXT_INFO, есть некоторые нюансы, о которых нужно знать. Я расскажу об этом в следующем сообщении в блоге:

    Почему CONTEXT_INFO () не возвращает точное значение, установленное SET CONTEXT_INFO?

  • Session_context является парой ключ / значение SQL_VARIANT. Это было введено в SQL Server 2016. Разделение значений для разных целей довольно приятно. Session_context доступен только для просмотра текущего сеанса.

    Установите это значение с помощью sp_set_session_context.
    Получите это значение с помощью SESSION_CONTEXT.

Следует учитывать одну вещь, касающуюся опции локальной временной таблицы и даже опции триггера отключения / включения: обе из них требуют некоторого количества блокировок и активности журнала. Оба этих варианта увеличивают вероятность возникновения конфликтов, даже если они минимальны. Два параметра «контекста» должны иметь меньший вес / только память.

Соломон Руцкий
источник
context_info - обезболивающее средство, когда вы хотите выполнить изменение производственных данных, это пригодится, особенно, если отключение триггера может привести к тому, что другие операции не будут запускать триггер.
Бижу Хосе