2012-04-09 23 views
10

In MS SQL Server 2008 R2, vogliamo un trigger pre-inserimento e pre-aggiornamento che controlli qualcosa e consenta o ripristini (tramite raiserror) l'inserimento/aggiornamento in esecuzione.Transazione di rollback dal trigger

Domanda: nel trigger INSTEAD OF. Si deve scrivere esplicitamente l'inserto o l'aggiornamento? Perché vogliamo che venga eseguito l'inserimento o l'aggiornamento predefinito e facciano solo il "precheck".

+0

Qual è la natura del "controllo preliminare"? I trigger hanno un overhead che gestisce le tabelle 'inserted' /' deleted'. È qualcosa che può essere applicato in un altro modo? –

+0

Tutto ciò di cui abbiamo bisogno è un vincolo univoco sulle triple (3 colonne) che ignorano le stringhe vuote in una colonna. – Cartesius00

+0

Quindi per tupla '(a, b, c)' se 'c' ha un valore di stringa vuota, vuoi ignorare completamente quella tupla ai fini del vincolo univoco? –

risposta

9

Sì.

È necessario scrivere l'esplicito INSERT o UPDATE.

Il trigger esegue INSTEAD OF l'operazione DML. Se si lascia vuoto il trigger, non si verificherà alcuna azione diversa dalle tabelle INSERTED/DELETED create e compilate in tempdb.

Sebbene dalla discussione nei commenti non utilizzerei affatto un trigger per questo, ma utilizzo un indice filtrato univoco CREATE UNIQUE INDEX ix ON T(a,b,c) WHERE c <> ''. È probabile che sia più performante ed evitare potenziali problemi di logica quando si ha a che fare con la concorrenza.

+0

Grazie. E quali sono gli alias per le righe inserite e aggiornate in quelle operazioni DML? – Cartesius00

+0

@James - 'INSERTED' e' DELETED' ma queste non sono tabelle. Il trigger scocca una volta per dichiarazione. Quindi "INSERT INTO Yourtable SELECT * FROM INSERTED" sarebbe un tipico trigger "INSTEAD OF INSERT". –

+0

E se il 'YourTable' contiene la colonna Identity e molte altre colonne. 'SELECT *' è un problema allora, non è vero? – Cartesius00

3

Probabilmente non si desidera un trigger INSTEAD OF a meno che non si desideri sostituire l'inserto o l'aggiornamento effettivi. Nel tuo caso, preferisci invece un trigger FOR INSERT, UPDATE.

Questo trigger di esempio stampa un messaggio sul client quando qualcuno tenta di aggiungere o modificare i dati nella tabella dei titoli.

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 

Si potrebbe anche usare cose come IF EXISTS o COLUMNS_UPDATED pure.

Qui un altro esempio che utilizza il rollback.

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 

non sono sicuro se si dispone di una transazione o no, ma nel tuo caso si vorrebbe qualcosa di simile al seguente:

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 
Problemi correlati