SQL Server: Як відключити тригер для оновлення лише для вашого поточного сеансу?


15

Я працюю над SQL Server 2008 R2.

У мене є таблична вигода, яка має ПІСЛЯ ВСТАВИТИ, ОНОВЛЕННЯ тригера під назвою 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
Якщо ви не можете змінити тригер, відповідь - ні.
jyao

Відповіді:


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

@CaM - Я припускаю, що оновлення одного рядка не займе занадто довго, якщо ОП швидко здійснить або скасує транзакцію. Сподіваємось, індекс є benefit_id:)
Скотт Ходгін

мені дуже сподобалося це рішення, оскільки я не повинен вносити жодних змін до тригера
серп

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_sesions

  • sys.sysпроцеси

Ідеологія тут полягає в тому, що бінарний рядок, який ви встановлюєте, піддається лише поточному сеансу, тож коли тригер виконується під час вашого сеансу, він побачить обсяг та змінні налаштування Context_infoфункції, і він перейде до вхідної частини тригера замість цього.

Варіант 2: Таблиця темп

Іцзік Бен-Ган має чудове рішення у своїй книзі "Всередині Microsoft SQL Server 2008 Програмування T-SQL: Програмування T-SQL", яка також є у його пізнішій книзі T-SQL Querying . Основна проблема з цим над 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

2
Я б використовував контекст_info () замість темп-таблиці в тригері. Іншим словом, якщо тригер виявить, що контекст_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_sesions

    Залежно від типу цінності, яку ви зберігаєте CONTEXT_INFO, слід пам’ятати про деякі нюанси. Я висвітлюю це в наступній публікації блогу:

    Чому CONTEXT_INFO () не повертає точне значення, встановлене SET CONTEXT_INFO?

  • Session_context - пара SQL_VARIANTзначень ключ / значення . Це було введено в SQL Server 2016. Розмежування значень для різних цілей є досить приємним. Session_context бачить лише поточний сеанс.

    Встановити це значення через sp_set_session_context
    Отримати це значення за допомогою SESSION_CONTEXT

Одне, що слід врахувати стосовно локальної можливості тимчасової таблиці та навіть опції відключення / включення тригера: обидва вимагають деякої кількості блокування та активності журналу трансакцій. Обидва ці варіанти збільшують потенціал до суперечок, навіть якщо вони мінімальні. Два "контекстні" варіанти повинні бути легшими за вагою / лише для пам'яті.


контекст_info - це знеболюючий засіб, коли ви хочете запустити зміну виробничих даних, це стане в нагоді, особливо вимкнення тригера може призвести до того, що інші операції не спрацьовують.
Biju jose
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.