2012-04-09 16 views
10

En MS SQL Server 2008 R2, queremos un desencadenante previo a la inserción y preactualización que verifique algo y permita o restituya (mediante raiserror) la inserción/actualización en ejecución.Revertir la transacción desde el desencadenante

Pregunta: En INSTEAD OF disparador. ¿Realmente uno tiene que escribir explícitamente la inserción o actualización? Porque queremos que se realice la inserción o actualización predeterminada y solo haga la "comprobación previa".

+0

¿Cuál es la naturaleza de la "comprobación previa"? Los disparadores tienen una sobrecarga que maneja las tablas 'inserted' /' deleted'. ¿Es algo que puede aplicarse de otra manera? –

+0

Todo lo que necesitamos es una restricción única sobre triples (3 columnas) ignorando cadenas vacías en una columna. – Cartesius00

+0

Entonces, para tuple '(a, b, c)' si 'c' tiene un valor de cadena vacía, ¿quiere ignorar por completo esa tupla a los efectos de la restricción única? –

Respuesta

9

Sí.

Necesita escribir explícitamente INSERT o UPDATE.

El disparador ejecuta INSTEAD OF la operación DML. Si deja el activador en blanco, no se realizará ninguna acción que no sean las tablas INSERTED/DELETED que se están creando y rellenando en tempdb.

Aunque por discusión en los comentarios, no utilizaría un activador para esto en absoluto, pero usaría un índice único filtrado CREATE UNIQUE INDEX ix ON T(a,b,c) WHERE c <> ''. Es probable que esto sea más eficaz y evite posibles problemas de lógica cuando se trata de concurrencia.

+0

Gracias. ¿Y cuáles son los alias para las filas insertadas y actualizadas en esas operaciones DML? – Cartesius00

+0

@James - 'INSERTED' y' DELETED', pero estas son tablas, no filas. El disparador se dispara una vez por declaración. Así que 'INSERT INTO YourTable SELECT * FROM INSERTED' sería un disparador típico 'INSTEAD OF INSERT'. –

+0

Y qué pasa si el 'YourTable' contiene una columna de identidad y muchas otras columnas. 'SELECT *' es un problema, ¿no? – Cartesius00

3

Probablemente no desee un activador INSTEAD OF a menos que desee reemplazar la inserción o actualización real. En su caso, desea un activador FOR INSERT, UPDATE en su lugar.

Este disparador de ejemplo imprime un mensaje al cliente cuando alguien intenta agregar o cambiar datos en la tabla de títulos.

USE pubs 
IF EXISTS (SELECT name FROM sysobjects 
     WHERE name = 'reminder' AND type = 'TR') 
    DROP TRIGGER reminder 
GO 
CREATE TRIGGER reminder 
ON titles 
FOR INSERT, UPDATE 
AS RAISERROR ('inserts and updates to the titles table is not allowed', 16, 1) 
GO 

También se puede usar cosas como IF EXISTSCOLUMNS_UPDATED o así.

Aquí hay otro ejemplo que usa la reversión.

USE pubs 
IF EXISTS (SELECT name FROM sysobjects 
     WHERE name = 'employee_insupd' AND type = 'TR') 
    DROP TRIGGER employee_insupd 
GO 
CREATE TRIGGER employee_insupd 
ON employee 
FOR INSERT, UPDATE 
AS 
/* Get the range of level for this job type from the jobs table. */ 
DECLARE @min_lvl tinyint, 
    @max_lvl tinyint, 
    @emp_lvl tinyint, 
    @job_id smallint 
SELECT @min_lvl = min_lvl, 
    @max_lvl = max_lvl, 
    @emp_lvl = i.job_lvl, 
    @job_id = i.job_id 
FROM employee e INNER JOIN inserted i ON e.emp_id = i.emp_id 
    JOIN jobs j ON j.job_id = i.job_id 
IF (@job_id = 1) and (@emp_lvl <> 10) 
BEGIN 
    RAISERROR ('Job id 1 expects the default level of 10.', 16, 1) 
    ROLLBACK TRANSACTION 
END 
ELSE 
IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl) 
BEGIN 
    RAISERROR ('The level for job_id:%d should be between %d and %d.', 
     16, 1, @job_id, @min_lvl, @max_lvl) 
    ROLLBACK TRANSACTION 
END 

no estoy seguro de si tiene una transacción o no, pero en su caso, usted podría querer algo como lo siguiente:

USE myDatabase 

    IF EXISTS (SELECT name FROM sysobjects 
      WHERE name = 'myTable' AND type = 'TR') 
     DROP TRIGGER tr_myTrigger 
    GO 
    CREATE TRIGGER tr_myTrigger 
    ON myTable 
    FOR INSERT, UPDATE 
    AS 

if(exists(select * from inserted where rtrim(c) <> '')) 
begin 
    -- check to make sure the insert(s) are unique 

    if(exists(
     select * from inserted i 
     join dbo.myTable t on i.a = t.a and i.b = t.b and i.c = t.c) 

    begin 
     raiserror('Duplicate(s) found', 16, 1) 
     rollback transaction 
    end 
end 
Cuestiones relacionadas