SQL Server: ¿Cómo deshabilitar el disparador para una actualización solo para su sesión actual?

15

Estoy trabajando en SQL Server 2008 R2.

Tengo un beneficio de tabla que tiene un desencadenador DESPUÉS DE INSERTAR, ACTUALIZAR, llamado tiu_benefit .

Quiero escribir una declaración de ACTUALIZACIÓN para que esta tabla actualice 1 fila, pero no quiero que se active su desencadenante. Sé que puedo desactivar el activador antes de ACTUALIZAR y luego activar el activador después de ACTUALIZAR:

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

Pero este activador de activación y desactivación afectará a todos los usuarios conectados actualmente. Por lo tanto, existe la posibilidad de que otro usuario ejecute una ACTUALIZACIÓN / INSERCIÓN mientras mi secuencia de comandos está deshabilitada, lo que no es bueno. Es por eso que solo quiero deshabilitar y habilitar el disparador para mi sesión actual. ¿Es posible? En caso afirmativo, por favor diga cómo.

Gracias

srh
fuente
1
Si no puede modificar su disparador, la respuesta es no.
jyao

Respuestas:

6

Hice algunas pruebas sobre esto y creo que estaría bien si ejecuta su proceso en una sola transacción.

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 

En mis pruebas, solo resalté y ejecuté el BEGIN TRANSACTIONy el DISABLE TRIGGERprimero. Entonces abrí un nuevo (segundo) ventana de consulta y trató de ejecutar varias instrucciones DML ( SELECT, INSERT, UPDATE DELETE) en contra de la tabla base. Todos los intentos de acceder a la tabla base en la segunda ventana de consulta esperaron en los bloqueos mantenidos por la ventana con la transacción explícita. Una vez que comprometí (o revertí) mi transacción explícita, la segunda ventana pudo acceder a la tabla.

Scott Hodgin
fuente
Esto funcionará, pero los bloqueos pueden causar problemas involuntarios aguas abajo, dependiendo de cuánto tiempo mantenga abierta la transacción.
CaM
@CaM: supongo que una actualización de una fila no llevaría demasiado tiempo suponiendo que el OP se confirma o revierte la transacción rápidamente. Con suerte, hay un índice en benefit_id:)
Scott Hodgin
Realmente me gustó esta solución ya que no tengo que hacer ningún cambio en el gatillo
srh
18

Para resolver su problema, debemos adoptar un enfoque programático del problema. Hay dos rutas que puedes seguir aquí. La razón por la que necesita estos enfoques es porque no puede deshabilitar un desencadenador para una declaración particular, solo puede deshabilitarse para la totalidad de la tabla.

Opción 1: Context_Info ()

Samuel Vanga en MS SQL Tips tuvo un gran ejemplo:

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

Ahora, cuando Samuel no quiere que se ejecute el disparador, usan esto:

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

Context_Info utiliza las siguientes vistas del sistema para obtener información sobre la sesión actual:

  • sys.dm_exec_requests

  • sys.dm_exec_sessions

  • sys.sysprocesses

La ideología aquí es que la cadena binaria que está configurando está expuesta solo a la sesión actual, por lo que cuando el disparador se ejecute durante su sesión, verá el alcance y la configuración variable de la Context_infofunción y saltará a la parte de escape del disparador en lugar.

Opción 2: tabla temporal

Itzik Ben-Gan tiene una gran solución en su libro "Inside Microsoft SQL Server 2008 Programación T-SQL: Programación T-SQL", que también se encuentra en su libro posterior T-SQL Querying . El principal problema con esto sobre la context_infofunción es la sobrecarga menor de TempDB.

Para estropear la sorpresa pero no arruinar la trama de los libros (sentí que vale la pena comprarlos y leerlos), alterará su gatillo.

Su disparador debe realizar una verificación para una tabla temporal. Si la tabla temporal existe, el desencadenador debe saber que finaliza y no realiza las acciones.

En la declaración de actualización que desea realizar, cree primero la tabla temporal. Se verá en la misma transacción que el activador y hará que el activador ignore su estado de cuenta.

Ejemplo de disparador:

CREATE TRIGGER TRIGGERNAME ON TABLENAME for INSERT AS

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

Ejemplo de declaración inicial cuando no desea que se ejecute el desencadenador:

CREATE TABLE #FAKETEMPTABLE(col1 SMALLINT);

Poniéndolo por completo para su ejemplo:

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
fuente
2
Usaría context_info () en lugar de la tabla temporal en el desencadenador. En otras palabras, si un activador detecta que context_info devuelve un valor específico, el activador funcionaría en consecuencia. Puede consultar la pregunta SO relevante aquí: stackoverflow.com/questions/3025662/…
jyao
1
También podría realizar una verificación similar a la de context_infousar original_login()para decirle al gatillo que nunca se ejecute si una persona específica está presionando el gatillo.
Kenneth Fisher
2

Yo usaría cualquiera CONTEXT_INFOo el más nuevo SESSION_CONTEXT. Ambos son valores basados ​​en sesión.

  • CONTEXT_INFOEs un VARBINARY(128)valor único . Esto ha estado disponible desde que al menos SQL Server 2000. CONTEXT_INFOes visible para cualquier persona, VIEW SERVER STATEya que es un campo devuelto por el sys.dm_exec_sessionsDMV. He usado este antes y funciona bastante bien.

    Establecer mediante SET CONTEXT_INFO
    Obtener mediante CONTEXT_INFO () o sys.dm_exec_sessions

    Dependiendo del tipo de valor que está almacenando CONTEXT_INFO, hay algunos matices a tener en cuenta. Cubro eso en la siguiente publicación de blog:

    ¿Por qué CONTEXT_INFO () no devuelve el valor exacto establecido por SET CONTEXT_INFO?

  • Session_context es un par de SQL_VARIANTvalores clave / valor . Esto se introdujo en SQL Server 2016. La separación de valores para diferentes propósitos es bastante agradable. Session_context solo es visible para la sesión actual.

    Establezca este valor a través de sp_set_session_context
    Obtenga este valor a través de SESSION_CONTEXT

Una cosa a tener en cuenta con respecto a la opción de tabla temporal local e incluso la opción de activación / desactivación de activación: ambas requieren cierta cantidad de actividad de bloqueo y de registro de transferencia. Ambas opciones aumentan el potencial de contención, aunque sea mínimamente. Las dos opciones de "contexto" deben ser más ligeras / solo de memoria.

Solomon Rutzky
fuente
context_info es un analgésico, siempre que desee ejecutar un cambio en los datos de producción, esto es útil, especialmente deshabilitar el disparador puede hacer que otras operaciones no disparen el disparador.
Biju jose