sql >> Base de Datos >  >> RDS >> Sqlserver

Activadores de SQL Server:Activadores DML

En SQL Server, los disparadores son objetos de la base de datos que se ejecutan cada vez que ocurre un evento disparador en la base de datos o el servidor.

Los disparadores juegan un papel clave en el cumplimiento de los requisitos comerciales, como alertar a las personas objetivo, iniciar un trabajo u otras operaciones. Dado que los disparadores pueden manejar muchas de estas operaciones, debemos definirlas con cuidado para evitar impactos en el rendimiento.

En este artículo, examinaremos los disparadores, los tipos de disparadores y varias opciones de disparadores disponibles. Además, exploraremos las precauciones necesarias al usar activadores DML.

Activadores en SQL

Un disparador es un tipo especial de procedimiento almacenado que se ejecuta en eventos definidos, ejecutando el script definido en el cuerpo del disparador. Hay varios tipos de disparadores:

  • Disparadores DML – para realizar operaciones DML como comandos INSERTAR, ACTUALIZAR y ELIMINAR en tablas.
  • Disparadores DDL – para realizar operaciones DDL como comandos CREATE, ALTER y DROP en cualquier objeto en la base de datos o el servidor.
  • Activadores de inicio de sesión – por el intento de iniciar sesión en una instancia de SQL Server durante el evento LOGON.

Disparadores DML en SQL Server

Los activadores DML son aquellos activados por comandos DML (INSERTAR, ACTUALIZAR o ELIMINAR) en tablas o vistas. Podemos crear dichos disparadores en esas tablas o vistas solo donde residen los datos para que acepten comandos DML en ellos.

Según el momento de activación/invocación, los activadores DML pueden ser de los siguientes tipos:

  • PARA o DESPUÉS Tipo de disparador:el disparador se invoca después de completar con éxito la instrucción DML en una tabla o vista. Nota:es posible crear el activador DESPUÉS solo en tablas, no en vistas.
  • EN LUGAR DE Tipo de activador:el activador se invocará antes (EN LUGAR DE) que el script DML se ejecute en la tabla o vista.

SQL Server crea dos tablas lógicas o especiales denominadas INSERTED y ACTUALIZADO cada vez que se crean disparadores DML en tablas o vistas. Estas tablas lógicas ayudan a identificar los cambios de registro que ocurren a través de las operaciones INSERTAR/ACTUALIZAR/ELIMINAR. De esta forma, garantiza que los disparadores DML funcionen de manera efectiva.

  • INSERTADO la tabla lógica almacena copias de nuevos registros de registros modificados durante las operaciones INSERTAR y ACTUALIZAR. Cuando se agrega un nuevo registro a la tabla real, también se agrega a la tabla INSERTADA. De manera similar, cualquier cambio en los registros existentes a través de la instrucción UPDATE mueve los valores más recientes a la tabla INSERTED y los valores más antiguos a la tabla lógica DELETED.
  • ELIMINADO la tabla lógica almacena copias de valores anteriores durante las operaciones UPDATE y DELETE. Cada vez que se actualiza un registro, los valores más antiguos se copian en la tabla DELETED. Cada vez que se elimina un registro de la tabla real, los registros se insertan en la tabla ELIMINADO.

SQL Server tiene funciones integradas COLUMN_UPDATED() y ACTUALIZAR() para identificar la presencia de operaciones INSERTAR o ACTUALIZAR en la columna en particular.

  • COLUMN_UPDATED() devuelve valores varbinary de columnas que se vieron afectadas por las operaciones INSERT o UPDATE.
  • ACTUALIZAR() acepta el nombre de la columna como un parámetro de entrada y devuelve la información si esa columna tiene algún cambio de datos como parte de las operaciones INSERTAR o ACTUALIZAR.

El NO PARA LA REPLICACIÓN La propiedad se puede utilizar en activadores DML para evitar activarlos por cambios que se produzcan a través del proceso de replicación.

Los disparadores DML también se pueden crear con .Net Framework Common Language Runtime (CLR).

El sistema DMV sys.triggers almacena la lista de todos los disparadores en el ámbito de la base de datos. Podemos usar la siguiente consulta para obtener los detalles de todos los activadores DML dentro de una base de datos:

SELECT * 
FROM sys.triggers
WHERE type = 'TR';

Las definiciones del disparador DML se pueden ver si el disparador no está cifrado. Usamos cualquiera de las siguientes opciones:

sys.sql_modules

SELECT OBJECT_SCHEMA_NAME(object_id, db_id()) Schema_name, OBJECT_NAME(object_id) Trigger_Name, definition
FROM sys.sql_modules  
WHERE object_id = OBJECT_ID(<trigger_name>);   

OBJECT_DEFINITION() función

SELECT OBJECT_DEFINITION (OBJECT_ID(<trigger_name>)) AS ObjectDefinition; 

sp_helptext procedimiento almacenado

EXEC sp_helptext '<trigger_name>';

Todos los eventos DML posibles están disponibles en sys.events mesa. Podemos verlos usando la siguiente consulta:

SELECT * 
FROM sys.events;

Sintaxis del disparador DML

CREATE TRIGGER <trigger_name>
ON <schema_name.table_name | schema_name.view_name > 
[ WITH <DML_trigger_option> [ ,...n ] ]  
{ FOR | AFTER | INSTEAD OF} <event_type>
AS { sql_statement | EXTERNAL NAME <method specifier> }  

Para propósitos de demostración, he creado dos tablas llamadas Ventas y Historial de ventas con pocas columnas en la base de datos de prueba:

CREATE TABLE Sales (SalesId int IDENTITY NOT NULL, SalesDate datetime, Itemcount int, price money);
CREATE TABLE SalesHistory (SalesId int NOT NULL, SalesDate datetime, Itemcount int, price money, ChangeType varchar(10), ChangeDate datetime DEFAULT GETDATE(), ChangedUser varchar(100) DEFAULT SUSER_NAME());
GO

Como puede ver, el Historial de ventas La tabla tiene 3 columnas adicionales para rastrear la fecha de modificación y el nombre de usuario que invocó el cambio. Si es necesario, podemos tener una columna de identidad más definida y convertirla también en una clave principal.

INSERTAR activador

Creamos un disparador INSERT simple en Ventas tablas para INSERTAR cualquier cambio de registro nuevo en SalesHistory mesa. Utilice el siguiente script:

CREATE TRIGGER TR_INS_Sales ON Sales
FOR INSERT 
AS
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    	,SalesDate
	,Itemcount
	,price
	,'INSERT'
  FROM inserted
END
GO

Para explicar la sintaxis del disparador, hemos creado un disparador DML llamado TR_INS_Sales en las Ventas mesa. Debe activar el activador solo para las operaciones INSERT:insertar registros en el Historial de ventas tabla de la tabla insertada.

Como sabemos, insertado es una tabla lógica que captura los cambios que ocurren en la tabla Fuente (la tabla Ventas tabla en nuestro caso).

Podemos ver otra tabla lógica especial eliminada en el activador de ACTUALIZACIÓN porque eliminó la tabla no es aplicable para activadores INSERT.

Agreguemos un nuevo registro para verificar si los registros se insertan en el Historial de ventas mesa automáticamente.

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-01-01', 5, 100);

Aunque hemos insertado solo un registro en Ventas tabla, obtenemos 2 líneas de la 1 fila afectada mensaje. El segundo registro aparece debido a la operación INSERT como parte del activador invocado por la actividad INSERT en Ventas tabla:insertar un registro en el Historial de ventas mesa.

Verifiquemos los registros tanto en Ventas y Historial de ventas tablas:

SELECT * 
FROM Sales

SELECT * 
FROM SalesHistory

Podemos ver que ChangeDate y Usuario cambiado se completan automáticamente. Es porque diseñamos nuestra Historia tabla con los valores predeterminados como GETDATE() y SUSER_NAME() .

Los usuarios finales pueden ver a través de Trigger o algún otro medio que su INSERT fue auditado a través de la 1 fila adicional afectada mensaje. Si desea monitorear los cambios sin informar a los usuarios, debe aplicar SET ROWCOUNT ON dominio. Suprime los resultados que se muestran para las operaciones DML que ocurren dentro del disparador.

CAMBIEMOS nuestro disparador usando el script con SET ROWCOUNT ON opción y verla en acción:

ALTER TRIGGER TR_INS_Sales ON Sales
FOR INSERT 
AS
BEGIN
SET NOCOUNT ON
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'INSERT'
  FROM inserted
END
GO

Ahora, insertamos otro registro en Ventas tabla:

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-02-01', 1, 50);

Podemos ver solo una 1 fila afectada mensaje. Por lo tanto, es posible que el público objetivo no reciba ninguna notificación de que sus acciones están bajo control.

Verifiquemos si se invocó el activador INSERT verificando Ventas y Historial de ventas mesas.

Sí, el evento INSERT en Ventas la tabla se activó con éxito. Los registros se insertaron en el Historial de ventas tabla sin notificar a los usuarios.

Por lo tanto, si crea disparadores con fines de auditoría, el SET NOCOUNT ON es necesario. Permite auditar sin alertar a nadie.

Activador de ACTUALIZAR

Antes de crear un activador de ACTUALIZACIÓN real en Ventas tabla, volvamos a referirnos a las tablas lógicas insertadas y eliminadas especiales. Cree un activador de ACTUALIZACIÓN de muestra en Ventas tabla:

CREATE TRIGGER TR_UPD_Sales ON Sales
FOR UPDATE 
AS
BEGIN
SELECT * FROM inserted
SELECT * FROM deleted
END
GO

El activador de ACTUALIZACIÓN se creó correctamente. Ahora, vamos a INSERTAR un nuevo registro incorrectamente. Más tarde lo actualizaremos para verificar el activador de ACTUALIZACIÓN en acción:

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-02-01', 1, 50);

Tenemos los siguientes registros en Ventas y Historial de ventas tabla:

Actualicemos SalesId =3 en las Ventas tabla con nuevos valores. Veremos los datos en las tablas insertadas y eliminadas:

UPDATE Sales
SET SalesDate = '2021-03-01'
	, Itemcount = 3
	, price = 500
WHERE SalesId = 3

Cuando se lleva a cabo la operación ACTUALIZAR, todos los valores nuevos o modificados estarán disponibles en la tabla insertada y los valores antiguos estarán disponibles en la tabla eliminada:

Ahora, modifiquemos el activador de ACTUALIZACIÓN con el siguiente script y verifiquemos que esté en acción:

ALTER TRIGGER TR_UPD_Sales ON Sales
FOR UPDATE 
AS
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'UPDATE'
  FROM inserted
END
GO

El activador de ACTUALIZACIÓN se modificó con éxito y podemos ejecutar el mismo script de ACTUALIZACIÓN nuevamente:

Ahora, podemos ver la 1 fila afectada mensaje dos veces. Indica la ejecución de la operación UPDATE sobre el Ventas tabla y la operación INSERT en el SalesHistory mesa. Verifiquemos esto seleccionando en ambas tablas:

La actividad de ACTUALIZACIÓN se rastreó en el Historial de ventas tabla como un nuevo registro. Antes de ese registro, tenemos otro que muestra cuándo se insertó el registro primero.

Desencadenador de ELIMINACIÓN

Hasta ahora, probamos el FOR o DESPUÉS tipo de activadores para las operaciones INSERTAR o ACTUALIZAR. Ahora, podemos intentar usar EN LUGAR DE tipo de disparador DML para la operación DELETE. Utilice el siguiente script:

CREATE TRIGGER TR_DEL_Sales ON Sales
INSTEAD OF DELETE 
AS
BEGIN
	RAISERROR ('Notify Sales Team', 16, 10);  
END
GO

El disparador DELETE se creó con éxito. Enviará un mensaje de error al cliente en lugar de ejecutar el comando ELIMINAR en Ventas mesa.

Intentemos eliminar el registro SalesID =3 de Ventas tabla usando el siguiente script:

DELETE FROM Sales
WHERE SalesId = 3

Hemos impedido que los usuarios eliminen registros de Ventas mesa. El activador generó un mensaje de error.

Verifiquemos también si el registro se eliminó de Ventas tabla y si hubo cambios en el Historial de ventas tabla:

Como hemos ejecutado la secuencia de comandos del disparador antes de la declaración DELETE real usando el disparador INSTEAD OF, la operación DELETE en SalesId=3 no tuvo éxito en absoluto. Por lo tanto, no se reflejaron cambios en las Ventas y Historial de ventas mesa.

Modifiquemos el disparador usando el siguiente script para identificar el intento de ELIMINAR en la tabla:

ALTER TRIGGER TR_DEL_Sales ON Sales
INSTEAD OF DELETE 
AS
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'DELETE ATP'
  FROM deleted 
END
GO

El activador se modificó con éxito. Eliminemos el SalesId =3 registro de Ventas mesa de nuevo:

La ejecución de la declaración DELETE muestra la 1 fila afectada mensaje dos veces. Revisemos los registros en Ventas y Historial de ventas tablas para ver qué sucede exactamente allí:

La lógica utilizada en el Activador DELETE era capturar cualquier intento de DELETE en la tabla sin eliminar realmente el registro de Ventas. tabla usando EN LUGAR DE generar. Podemos confirmar que el registro no se eliminó de Ventas y se insertó un nuevo registro en el Historial de ventas mesa.

Un disparador único para manejar las operaciones INSERTAR, ACTUALIZAR y ELIMINAR

Hasta ahora, hemos creado 3 disparadores para manejar las operaciones INSERTAR, ACTUALIZAR y ELIMINAR en una sola tabla. Si tenemos múltiples factores desencadenantes, sería difícil gestionarlos, especialmente si no están debidamente documentados. Puede haber problemas de rendimiento si los desarrolladores usaron una lógica contradictoria en varios disparadores.

Personalmente, recomiendo usar un solo activador con toda la lógica combinada para evitar posibles problemas de rendimiento o pérdida de datos. Podemos intentar combinar 3 disparadores en un solo disparador para un mejor rendimiento. Pero antes de hacerlo, examinemos cómo DESACTIVAR los disparadores existentes y cómo deshabilitarlos o habilitarlos.

Suelta el gatillo

Para fusionar 3 activadores en uno solo, primero debemos SOLTAR estos 3 activadores. Es posible a través de los enfoques SSMS y T-SQL.

En SSMS, expanda la Prueba base de datos > Mesas > Ventas tabla> Disparadores .

Podemos ver nuestros 3 disparadores creados hasta ahora:

Para soltar un disparador, simplemente haga clic con el botón derecho en él> Eliminar > OK .

Si prefiere usar T-SQL, consulte la siguiente sintaxis para soltar el activador:

DROP TRIGGER <trigger_name>

Existe el TR_INS_Sales disparador que creamos en Ventas mesa. El guión será:

DROP TRIGGER TR_INS_Sales

Importante :al descartar una tabla, se eliminan todos los activadores de forma predeterminada.

Deshabilitar y habilitar activador

En lugar de soltar el activador, podemos desactivarlo temporalmente con Desactivar gatillo opción a través de SSMS o T-SQL.

En SSMS, haga clic derecho en el Nombre del disparador> Deshabilitar . Una vez deshabilitado, el activador no se activará hasta que lo vuelva a habilitar.

Mientras el disparador está funcionando, el botón Habilitar la opción está atenuada. Cuando lo deshabilita, el Habilitar La opción se volverá visible y activa.

Si prefiere usar T-SQL, puede deshabilitar y habilitar disparadores usando los siguientes scripts:

-- To Disable all triggers on a specific table
DISABLE TRIGGER ALL ON <table_name>;

-- To Disable a specific trigger on a table
DISABLE TRIGGER <trigger_name> ON <table_name>;

-- To Enable all triggers on a specific table
ENABLE TRIGGER ALL ON <table_name>;

-- To Enable a specific trigger on a table
ENABLE TRIGGER <trigger_name> ON <table_name>;

Para deshabilitar y habilitar nuestro TR_INS_Sales particular activar las Ventas tabla, usamos los siguientes scripts:

-- To Disable TR_INS_Sales trigger on Sales table
DISABLE TRIGGER TR_INS_Sales ON Sales;

-- To Enable TR_INS_Sales trigger on Sales table
ENABLE TRIGGER TR_INS_Sales ON Sales;

Por lo tanto, hemos aprendido a DROP , DESACTIVAR y HABILITAR disparadores Eliminaré 3 disparadores existentes y crearé un solo disparador que cubra las 3 operaciones o inserte, actualice y elimine usando el siguiente script:

DROP TRIGGER TR_INS_Sales
DROP TRIGGER TR_UPD_Sales
DROP TRIGGER TR_DEL_Sales
GO

CREATE TRIGGER TR_INS_UPD_DEL_Sales ON Sales
FOR INSERT, UPDATE, DELETE
AS
BEGIN
IF (SELECT COUNT (*) FROM deleted) = 0
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'INSERT'
  FROM inserted
END
ELSE IF (SELECT COUNT (*) FROM inserted) = 0
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'DELETE'
  FROM deleted
END
ELSE IF (UPDATE (SalesDate) OR UPDATE (ItemCount) OR UPDATE (Price))
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'UPDATE'
  FROM inserted
END 
END
GO

La creación del disparador único fue exitosa. Hemos utilizado la lógica para identificar la operación utilizando las tablas insertadas y eliminadas.

Para la operación INSERTAR, la tabla eliminada no se completará. Para la operación DELETE, la tabla insertada no se llenará. Podemos identificar estas operaciones fácilmente. Si estas 2 condiciones no coinciden, entonces es una operación de ACTUALIZACIÓN y podemos usar una instrucción ELSE simple.

He usado UPDATE() función para mostrar cómo funciona. Si hubiera alguna actualización en esas columnas, se activaría la acción del activador ACTUALIZAR. También podemos usar COLUMNS_UPDATED() función que discutimos anteriormente para identificar una operación de ACTUALIZACIÓN también.

Probemos nuestro nuevo activador insertando un nuevo registro:

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-04-01', 4, 400);

Verificación de registros en Ventas y Historial de ventas las tablas muestran los datos de la siguiente manera:

Intentemos actualizar SalesId =2 registro:

UPDATE Sales
SET price = 250
WHERE SalesId = 2;

Probemos un script DELETE a través de este procedimiento en SalesId =4 registro:

DELETE FROM Sales
WHERE SalesId = 4;

Como podemos notar, SalesId =4 fue eliminado de las Ventas tabla ya que esta es una PARA o DESPUÉS disparador, haciendo que la operación DELETE tenga éxito en Ventas y luego inserte un registro en el Historial de ventas mesa.

Propósito de los activadores DML

Los disparadores DML sirven de manera efectiva para los siguientes escenarios:

  1. Haga un seguimiento de los cambios históricos de las operaciones INSERTAR, ACTUALIZAR y ELIMINAR en una tabla específica.
  2. Audita los eventos DML que ocurren en una tabla sin exponer la actividad de auditoría a los usuarios.
  3. Evite que se produzcan cambios de DML en una tabla a través de EN LUGAR DE activa y alerta a los usuarios con un mensaje de error específico.
  4. Envíe notificaciones a las personas objetivo cuando alcance cualquier condición predefinida.
  5. Inicie el trabajo del Agente SQL Server o cualquier otro proceso cada vez que logre las condiciones predefinidas.

Y puede usarlos para cualquier otro requisito de lógica comercial que pueda implementar con declaraciones T-SQL.

Conclusión

El cuerpo del disparador DML es similar al procedimiento almacenado. Podemos implementar cualquier lógica comercial requerida, pero debemos tener cuidado al escribir esa lógica involucrada para evitar posibles problemas.

Aunque SQL Server admite la creación de varios disparadores en una sola tabla, es mejor consolidar en un solo disparador. De esta manera, puede mantener los disparadores fácilmente y solucionarlos más rápido. Siempre que se implementen disparadores DML con fines de auditoría, asegúrese de que SET NOCOUNT ON la opción se usa de manera efectiva.

En el próximo artículo, examinaremos los activadores DDL y los activadores de inicio de sesión.