2008-10-06 16 views
32

En SQL Server 2005, ¿hay alguna manera para que un desencadenador descubra qué objeto es responsable de disparar el desencadenador? Me gustaría utilizar esto para desactivar el desencadenante de un prodecure almacenado.¿Hay alguna manera de deshabilitar un desencadenador de SQL Server solo para un ámbito de ejecución particular?

¿Hay alguna otra manera de desactivar el activador solo para la transacción actual? Podría usar el siguiente código, pero si no me equivoco, también afectaría las transacciones concurrentes, lo que sería algo malo.

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 } [ ; ] 

Si es posible, me gustaría evitar la técnica de tener un campo "NOTRIGGER" en mi mesa y hacer un NoTrigger = null, porque me gustaría mantener la mesa lo más pequeño posible.

La razón por la que quisiera evitar el activador es porque contiene lógica que es importante para las actualizaciones manuales de la tabla, pero mi procedimiento almacenado se encargará de esta lógica. Debido a que este será un procedimiento muy utilizado, quiero que sea rápido.

Los activadores imponen una carga adicional en el servidor porque inician una transacción implícita. Tan pronto como se ejecuta un disparador, se inicia una nueva transacción implícita, y cualquier recuperación de datos dentro de una transacción mantendrá bloqueos en las tablas afectadas.

Desde: http://searchsqlserver.techtarget.com/tip/1,289483,sid87_gci1170220,00.html#trigger

+1

puede usar la función ** Exec ** para activar y activar desencadenantes desde un procedimiento almacenado. Ejemplo: 'EXEC ('ENABLE TRIGGER dbo.TriggerName en dbo.TriggeredTable')' –

Respuesta

52

acabo de ver este artículo destacado recientemente en el boletín central de SQL Server y parece ofrecer de una manera que usted puede encontrar útil el uso de la CONTEXT_INFO en la conexión:

http://www.mssqltips.com/tip.asp?tip=1591


EDIT Terrapin:

el enlace de arriba incluye el siguiente código:

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 

Si desea evitar que el gatillo de ser ejecutado, puede hacer lo siguiente:

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

Realmente me gustaría que hubiera una mejor forma para las variables/estado de la sesión (?): ¿Qué ocurre si 'Context_Info' ya se usa para otros fines? (Por ejemplo, la implementación de múltiples tennancy.) :( –

+1

@pst Bueno tiene 128 bytes de datos binarios, por lo que es posible poner todo en una estructura de datos que hay para diferentes propósitos y se envuelve con funciones de acceso. –

+0

Uf, se siente tan sucio! –

4

ALTER TABLE TBL DISABLE TRIGGER trg

http://doc.ddart.net/mssql/sql70/aa-az_5.htm

que no entienden el significado de su primera párrafo embargo

+0

Mi primer párrafo: en mi trigger, ¿hay alguna función que pueda usar para obtener el nombre del proceso almacenado que activó mi trigger? Algo así como usar 'if (trigger_nestlevel (object_id ('NameOfTrigger')) = 0)' para asegurarse de que el disparador no se llame a sí mismo – Seibar

1

No desactive el gatillo. Tiene razón de que se deshabilitará para cualquier transacción simultánea.

¿Por qué desea desactivar el activador? ¿Qué hace? ¿Por qué está causando el gatillo un problema? Por lo general, es una mala idea desactivar un tigger desde una perspectiva de integridad de datos.

6

Si su desencadenador está causando problemas de rendimiento en su aplicación, entonces el mejor enfoque es eliminar todas las actualizaciones manuales de la tabla y requerir que todas las actualizaciones pasen por los procedimientos almacenados de inserción/actualización que contienen la lógica de actualización correcta. Entonces puedes quitar el gatillo por completo.

Sugiero que se denieguen los permisos de actualización de tabla si nada funciona.

Esto también resuelve el problema del código duplicado. Duplicar el código en la actualización SP y en el desencadenante es una violación de los buenos principios de ingeniería de software y será un problema de mantenimiento.

+0

Totalmente en desacuerdo. Hay muchas formas en que los datos pueden modificarse y las reglas comerciales deben aplicarse a nivel de la base de datos. Si el desencadenador no funciona bien, se debe volver a escribir, pero nunca se debe quitar o deshabilitar. Puede crear problemas de integridad de datos muy malos con su enfoque. – HLGEM

+0

El procedimiento almacenado ya mantiene la integridad de los datos, y el acceso a la tabla puede limitarse de modo que cualquier proceso que desee actualizarlo debe usar el proceso. Si permite el acceso a la mesa de muchas maneras, el desencadenante es solo un síntoma del problema, no la solución. –

+0

si es posible hacer lo que sugiere Jeffrey, esa sería la mejor solución. –

2

Dado que usted indica que el desencadenador contiene lógica para manejar todas las actualizaciones, incluso las manuales, entonces debería ser donde resida la lógica. El ejemplo que mencionas, en el que un procedimiento almacenado "se encargará de esta lógica" implica un código duplicado. Además, si quiere estar seguro de que cada instrucción UPDATE tiene aplicada esta lógica independientemente del autor, entonces el desencadenador es el lugar adecuado. ¿Qué sucede cuando alguien crea un procedimiento pero olvida duplicar la lógica una vez más? ¿Qué sucede cuando es el momento de modificar la lógica?

1

Considere volver a escribir el desencadenador para mejorar el rendimiento si el problema es el rendimiento.

0

Estoy de acuerdo con algunas otras respuestas. No deshabilite el disparador.

Esto es pura opinión, pero evito factores desencadenantes como la peste. He encontrado muy pocos casos en los que se haya utilizado un desencadenador para aplicar las reglas de la base de datos. Hay casos obvios en mi experiencia, y solo tengo mi experiencia para hacer esta afirmación. Normalmente he visto desencadenantes utilizados para insertar algunos datos relacionales (lo que se debe hacer desde la lógica comercial), insertar datos en la tabla de informes, es decir, desnormalizar los datos (que se pueden hacer con un proceso fuera de la transacción) o transformar los datos de alguna manera.

Existen usos legítimos para los factores desencadenantes, pero creo que en la programación comercial cotidiana son pocos y distantes. Puede que esto no ayude en su problema actual, pero podría considerar eliminar el gatillo por completo y realizar el trabajo que el activador está haciendo de alguna otra manera.

+0

"¿Las sentencias DDL no se ejecutan en el contexto de una transacción?" Puedes probar y ver por ti mismo que esto está mal. BTW, la comparación SQL de Red Gate puede generar una secuencia de comandos de implementación que se ejecuta como una sola transacción. –

+0

Tienes razón. Creo que mi tiempo en Oracle-land debe haberme envenenado. –

1

He hablado un poco de esto. Por un lado, soy muy antiactivador, sobre todo porque es un lugar más en el que puedo buscar la ejecución del código en mi tabla, además de los motivos indicados en el artículo vinculado en la publicación de la pregunta. Por otro lado, si tiene lógica para aplicar reglas comerciales estables e inmutables o acciones de tabla cruzada (como mantener una tabla de historial) sería más seguro tener esto en un desencadenante para que los autores de procedimientos y programadores no Necesito lidiar con eso, simplemente funciona.

Por lo tanto, mi recomendación es poner la lógica necesaria en su desencadenador en lugar de en este proceso que, inevitablemente crecerá a varios procesos con la misma exención.

0

acabo enfrentado el mismo problema y se le ocurrió la siguiente solución, que funciona para mí.

  1. Crear una tabla de base de datos permanente que contiene un registro para cada disparador que desea desactivar (por ejemplo refTriggerManager); cada fila contiene el nombre del disparador (por ejemplo, strTriggerName = 'myTrigger') y un indicador de bit (por ejemplo, blnDisabled, por defecto a 0).

  2. Al principio del cuerpo del disparador, busque strTriggerName = 'myTrigger' en refTriggerManager. Si blnDisabled = 1, regrese sin ejecutar el resto del código de activación, de lo contrario continúe el código de activación hasta su finalización.

  3. En el procedimiento almacenado en el que desea desactivar el gatillo, haga lo siguiente:


iniciar la transacción

ACTUALIZACIÓN refTriggerManager SET blnDisabled = 1 DONDE strTriggerName = 'MyTrigger'

/* ACTUALIZA la tabla que posee 'myTrigger', pero que deseas deshabilitar. Dado que refTriggerManager.blnDisabled = 1, 'myTrigger' regresa sin ejecutar su código. */

ACTUALIZACIÓN refTriggerManager SET blnDisabled = 0 donde triggerName =

/* código de actualización final opcional 'MyTrigger' que dispara el gatillo. Como refTriggerManager.blnDisabled = 0, 'myTrigger' se ejecuta en su totalidad. */

confirmar la transacción


Todo esto tiene lugar dentro de una transacción, por lo que es aislado del mundo exterior y no afectará a otras actualizaciones de la tabla de destino.

¿Alguien ve algún problema con este enfoque?

Bill

+3

Esto se ve rota por 'LEA SNAPSHOT' comprometido, y en leerlo bloques COMMITTED' cada otra transacción estándar' LMD hasta que termine, evitando el uso efectivo de bloqueos a nivel de fila. Básicamente, has implementado una versión paralizada de la respuesta aceptada. Si esta no era una pregunta tan vieja, probablemente te daría -1 solo por la horrible notación húngara y la ausencia de formato. – Aaronaught

2

No estoy seguro si esto es una buena idea, pero parece que funciona para mí. La transacción debe evitar inserciones en la tabla de otros procesos mientras el desencadenador está deshabilitado.

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 
+0

Esa es una extraña manera de crear o actualizar un procedimiento almacenado existente (hay una edición no aprobado para cambiarlo a una simple gota/crear, y al reparar su error de bloque de copias en el PROCEDIMIENTO DE GOTA).¿Hay alguna ventaja de hacerlo de esta manera? ¡Gracias! – Rup

+0

Eso solo se usa como un complemento/actualización. Si abandona el procedimiento, puede estar seguro de que "CREAR PROCEDIMIENTO" funcionará. Lo he usado cuando almacenaba procedimientos almacenados en control de fuente. Cada archivo .sql es un script que se descarta, si existe, y luego el procedimiento almacenado actual. No es relevante para la respuesta. Si/Else podría usarse también, pero el contenido del procedimiento almacenado debería duplicarse. – Steve

+0

Claro, acabo de notarlo debido a la edición. Sí, generalmente tenemos "si el procedimiento existe, suelta, crea" - Simplemente no he visto "soltar, crear como 'seleccionar 1', actualizar" antes. – Rup

0

puede utilizar 'Exec' función diable y activar los disparadores de un procedimiento almacenado. Ejemplo: EXEC ('ENABLE TRIGGER dbo.TriggerName on dbo.TriggeredTable')

Cuestiones relacionadas