SQL Server: come disabilitare il trigger per un aggiornamento solo per la sessione corrente?


15

Sto lavorando su SQL Server 2008 R2.

Ho un vantaggio tabella che ha un trigger AFTER INSERT, UPDATE chiamato tiu_benefit .

Voglio scrivere un'istruzione UPDATE per questa tabella per aggiornare 1 riga, ma non voglio che il suo trigger si attivi. So che posso disabilitare il trigger prima dell'aggiornamento e quindi abilitare il trigger dopo l'aggiornamento:

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

Ma questo trigger di disabilitazione e abilitazione avrà effetto su tutti gli utenti che hanno effettuato l'accesso. Quindi c'è la possibilità che un altro utente esegua un UPDATE / INSERT mentre il trigger è disabilitato dal mio script che non va bene. Ecco perché voglio solo disabilitare e abilitare il trigger per la mia sessione corrente. È possibile? Se sì, per favore dì come.

Grazie


1
Se non è possibile modificare il trigger, la risposta è no.
jyao,

Risposte:


6

Ho fatto alcuni test su questo e penso che andresti bene se esegui il processo in un'unica transazione.

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 

Nei miei test, ho solo evidenziato ed eseguito il BEGIN TRANSACTIONe il DISABLE TRIGGERprimo. Sono quindi aperto un nuovo (secondo) finestra di query e cercato di eseguire varie istruzioni DML ( SELECT, INSERT, UPDATE DELETE) contro la tabella di base. Tutti i tentativi di accedere alla tabella di base nella seconda finestra della query sono stati attesi sui blocchi mantenuti dalla finestra con la transazione esplicita. Dopo aver eseguito il commit (o il rollback) della mia transazione esplicita, la seconda finestra è stata in grado di accedere alla tabella.


Questo funzionerà, ma i blocchi potrebbero causare problemi non intenzionali a valle, a seconda della durata della transazione aperta.
CaM,

@CaM - Suppongo che un aggiornamento di una riga non richiederebbe troppo tempo presupponendo che l'OP si impegni o ripristini rapidamente la transazione. Speriamo che ci sia un indice su benefit_id:)
Scott Hodgin,

questa soluzione mi è davvero piaciuta perché non devo apportare modifiche al grilletto
srh

18

Per risolvere il tuo problema, dobbiamo adottare un approccio programmatico al problema. Ci sono due percorsi che puoi seguire qui. Il motivo per cui sono necessari questi approcci è perché non è possibile disabilitare un trigger per una particolare istruzione, può essere disabilitato solo per l'intera tabella.

Opzione 1: Context_Info ()

Samuel Vanga su MS SQL Tips ha avuto un ottimo esempio:

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

Ora, quando Samuel non vuole eseguire il trigger, usano questo:

SET Context_Info 0x55555 
INSERT dbo.Table1 VALUES(100)

Context_Info utilizza le seguenti visualizzazioni di sistema per acquisire informazioni sulla sessione corrente:

  • sys.dm_exec_requests

  • sys.dm_exec_sessions

  • sys.sysprocesses

L'ideologia qui è che la stringa binaria che stai impostando è esposta solo alla sessione corrente, quindi quando il trigger viene eseguito durante la sessione, vedrà l'ambito e l'impostazione variabile della Context_infofunzione e salterà alla porzione di escape del trigger anziché.

Opzione 2: tabella temporanea

Itzik Ben-Gan ha una grande soluzione nel suo libro "Inside Microsoft SQL Server 2008 Programmazione T-SQL: programmazione T-SQL", che è anche nel suo libro T-SQL Querying . Il problema principale con questo oltre la context_infofunzione è il sovraccarico TempDB minore.

Per rovinare la sorpresa ma non rovinare la trama dei libri (ho sentito che vale la pena acquistare e leggere), altererai il tuo grilletto.

Il trigger dovrebbe eseguire un controllo per una tabella temporanea. Se esiste una tabella temporanea, il trigger dovrebbe sapere di terminare e non eseguire le azioni.

Nell'istruzione di aggiornamento che si desidera eseguire, creare prima la tabella temporanea. Verrà visualizzato nella stessa transazione del trigger e causerà al trigger di ignorare la tua dichiarazione.

Esempio di trigger:

CREATE TRIGGER TRIGGERNAME ON TABLENAME for INSERT AS

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

Esempio di istruzione iniziale quando non si desidera eseguire il trigger:

CREATE TABLE #FAKETEMPTABLE(col1 SMALLINT);

Mettendo tutto insieme per il tuo esempio:

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
Vorrei utilizzare context_info () invece della tabella temporanea nel trigger. In altre parole, se un trigger rileva il context_info restituisce un valore specifico, il trigger funzionerebbe di conseguenza. Si può fare riferimento alla relativa domanda SO qui: stackoverflow.com/questions/3025662/...
jyao

1
Potresti anche mettere un segno di spunta simile a quello context_infousato original_login()per dire al grilletto di non correre mai se una persona specifica sta colpendo il grilletto.
Kenneth Fisher,

2

Vorrei utilizzare uno CONTEXT_INFOo più recente SESSION_CONTEXT. Entrambi sono valori basati sulla sessione.

  • CONTEXT_INFOè un VARBINARY(128)valore singolo . Questo è disponibile da almeno SQL Server 2000. CONTEXT_INFOè visualizzabile da chiunque VIEW SERVER STATEin quanto è un campo restituito dal sys.dm_exec_sessionsDMV. L'ho usato prima e funziona abbastanza bene.

    Impostato tramite SET CONTEXT_INFO
    Ottieni tramite CONTEXT_INFO () o sys.dm_exec_sessions

    A seconda del tipo di valore in cui si sta memorizzando CONTEXT_INFO, ci sono alcune sfumature di cui tenere conto. Lo tratterò nel seguente post sul blog:

    Perché CONTEXT_INFO () non restituisce il valore esatto impostato da SET CONTEXT_INFO?

  • Session_context è una coppia chiave / valore di SQL_VARIANTvalori. Questo è stato introdotto in SQL Server 2016. La separazione dei valori per scopi diversi è abbastanza buona. Session_context è visualizzabile solo dalla sessione corrente.

    Imposta questo valore tramite sp_set_session_context
    Ottieni questo valore tramite SESSION_CONTEXT

Una cosa da considerare per quanto riguarda l'opzione della tabella temporanea locale e persino l'opzione di disabilitazione / abilitazione del trigger: entrambi richiedono una certa quantità di attività di blocco e registrazione del registro. Entrambe queste opzioni aumentano il potenziale di contesa, anche se in minima parte. Le due opzioni di "contesto" dovrebbero essere più leggere / solo memoria.


context_info è un antidolorifico, ogni volta che si desidera eseguire una modifica dei dati di produzione, ciò risulta utile, in particolare la disabilitazione del trigger può causare altre operazioni che non attivano il trigger.
Biju jose,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.